From abe7be913432053f6d419ea4ca4f9cd2be938bc7 Mon Sep 17 00:00:00 2001 From: Owen O'Malley Date: Tue, 19 May 2009 04:35:56 +0000 Subject: [PATCH] HADOOP-4687 Moving directories around git-svn-id: https://svn.apache.org/repos/asf/hadoop/core/branches/HADOOP-4687/core@776176 13f79535-47bb-0310-9956-ffa450edef68 --- lib/commons-cli-2.0-SNAPSHOT.jar | Bin 0 -> 258337 bytes lib/hsqldb-1.8.0.10.LICENSE.txt | 66 + lib/hsqldb-1.8.0.10.jar | Bin 0 -> 706710 bytes lib/jdiff/hadoop_0.17.0.xml | 43272 +++++++++++++ lib/jdiff/hadoop_0.18.1.xml | 44778 +++++++++++++ lib/jdiff/hadoop_0.18.2.xml | 38788 ++++++++++++ lib/jdiff/hadoop_0.18.3.xml | 38826 ++++++++++++ lib/jdiff/hadoop_0.19.0.xml | 43972 +++++++++++++ lib/jdiff/hadoop_0.19.1.xml | 44195 +++++++++++++ lib/jdiff/hadoop_0.20.0.xml | 52140 ++++++++++++++++ lib/kfs-0.2.2.jar | Bin 0 -> 11428 bytes lib/kfs-0.2.LICENSE.txt | 202 + src/test/org/apache/hadoop/cli/TestCLI.java | 450 + src/test/org/apache/hadoop/cli/testConf.xml | 18 + src/test/org/apache/hadoop/cli/testConf.xsl | 28 + .../apache/hadoop/cli/util/CLITestData.java | 136 + .../hadoop/cli/util/CommandExecutor.java | 111 + .../hadoop/cli/util/ComparatorBase.java | 39 + .../hadoop/cli/util/ComparatorData.java | 106 + .../hadoop/cli/util/ExactComparator.java | 34 + .../util/RegexpAcrossOutputComparator.java | 39 + .../hadoop/cli/util/RegexpComparator.java | 50 + .../hadoop/cli/util/SubstringComparator.java | 33 + .../hadoop/cli/util/TokenComparator.java | 49 + .../apache/hadoop/conf/TestConfiguration.java | 392 + .../conf/TestConfigurationSubclass.java | 102 + .../apache/hadoop/conf/TestGetInstances.java | 74 + .../hadoop/conf/empty-configuration.xml | 4 + .../filecache/TestDistributedCache.java | 77 + .../hadoop/fs/FileSystemContractBaseTest.java | 471 + .../hadoop/fs/TestChecksumFileSystem.java | 79 + .../apache/hadoop/fs/TestDFVariations.java | 63 + src/test/org/apache/hadoop/fs/TestDU.java | 95 + .../hadoop/fs/TestGetFileBlockLocations.java | 139 + .../apache/hadoop/fs/TestGlobExpander.java | 62 + .../hadoop/fs/TestLocalDirAllocator.java | 211 + .../apache/hadoop/fs/TestLocalFileSystem.java | 156 + .../fs/TestLocalFileSystemPermission.java | 157 + src/test/org/apache/hadoop/fs/TestPath.java | 152 + src/test/org/apache/hadoop/fs/TestTrash.java | 313 + .../hadoop/fs/TestTruncatedInputBug.java | 109 + .../hadoop/fs/kfs/KFSEmulationImpl.java | 150 + .../hadoop/fs/kfs/TestKosmosFileSystem.java | 204 + .../fs/loadGenerator/DataGenerator.java | 160 + .../fs/loadGenerator/LoadGenerator.java | 610 + .../fs/loadGenerator/StructureGenerator.java | 307 + .../fs/permission/TestFsPermission.java | 116 + .../hadoop/fs/s3/InMemoryFileSystemStore.java | 185 + .../fs/s3/Jets3tS3FileSystemContractTest.java | 31 + .../fs/s3/S3FileSystemContractBaseTest.java | 48 + .../org/apache/hadoop/fs/s3/TestINode.java | 60 + .../s3/TestInMemoryS3FileSystemContract.java | 31 + .../hadoop/fs/s3/TestS3Credentials.java | 36 + .../apache/hadoop/fs/s3/TestS3FileSystem.java | 50 + .../InMemoryNativeFileSystemStore.java | 198 + .../Jets3tNativeS3FileSystemContractTest.java | 30 + .../NativeS3FileSystemContractBaseTest.java | 59 + ...estInMemoryNativeS3FileSystemContract.java | 30 + .../apache/hadoop/http/TestGlobalFilter.java | 139 + .../apache/hadoop/http/TestServletFilter.java | 138 + .../org/apache/hadoop/io/RandomDatum.java | 108 + .../org/apache/hadoop/io/TestArrayFile.java | 155 + .../apache/hadoop/io/TestArrayWritable.java | 64 + .../apache/hadoop/io/TestBloomMapFile.java | 70 + .../apache/hadoop/io/TestBytesWritable.java | 95 + .../hadoop/io/TestDefaultStringifier.java | 113 + .../apache/hadoop/io/TestEnumSetWritable.java | 103 + .../apache/hadoop/io/TestGenericWritable.java | 178 + .../org/apache/hadoop/io/TestMD5Hash.java | 115 + .../org/apache/hadoop/io/TestMapFile.java | 124 + .../org/apache/hadoop/io/TestMapWritable.java | 132 + .../io/TestSequenceFileSerialization.java | 69 + .../org/apache/hadoop/io/TestSetFile.java | 157 + .../hadoop/io/TestSortedMapWritable.java | 102 + src/test/org/apache/hadoop/io/TestText.java | 266 + .../org/apache/hadoop/io/TestTextNonUTF8.java | 56 + src/test/org/apache/hadoop/io/TestUTF8.java | 86 + .../hadoop/io/TestVersionedWritable.java | 179 + .../org/apache/hadoop/io/TestWritable.java | 99 + .../apache/hadoop/io/TestWritableName.java | 107 + .../apache/hadoop/io/TestWritableUtils.java | 65 + .../apache/hadoop/io/compress/TestCodec.java | 249 + .../hadoop/io/compress/TestCodecFactory.java | 151 + .../hadoop/io/retry/TestRetryProxy.java | 170 + .../io/retry/UnreliableImplementation.java | 60 + .../hadoop/io/retry/UnreliableInterface.java | 42 + .../serializer/TestWritableSerialization.java | 95 + src/test/org/apache/hadoop/ipc/TestIPC.java | 243 + .../hadoop/ipc/TestIPCServerResponder.java | 150 + src/test/org/apache/hadoop/ipc/TestRPC.java | 391 + .../org/apache/hadoop/log/TestLogLevel.java | 78 + .../hadoop/metrics/TestMetricsServlet.java | 110 + .../hadoop/metrics/spi/TestOutputRecord.java | 38 + .../org/apache/hadoop/net/StaticMapping.java | 62 + src/test/org/apache/hadoop/net/TestDNS.java | 150 + .../hadoop/net/TestScriptBasedMapping.java | 46 + .../hadoop/net/TestSocketIOWithTimeout.java | 155 + .../org/apache/hadoop/record/FromCpp.java | 120 + .../org/apache/hadoop/record/RecordBench.java | 313 + .../org/apache/hadoop/record/TestBuffer.java | 124 + .../apache/hadoop/record/TestRecordIO.java | 199 + .../hadoop/record/TestRecordVersioning.java | 239 + src/test/org/apache/hadoop/record/ToCpp.java | 113 + .../security/TestAccessControlList.java | 104 + .../hadoop/security/TestAccessToken.java | 89 + .../TestUnixUserGroupInformation.java | 103 + .../authorize/TestConfiguredPolicy.java | 82 + .../apache/hadoop/test/CoreTestDriver.java | 63 + .../hadoop/util/TestCyclicIteration.java | 61 + .../apache/hadoop/util/TestGenericsUtil.java | 121 + .../apache/hadoop/util/TestIndexedSort.java | 361 + .../util/TestProcfsBasedProcessTree.java | 234 + .../org/apache/hadoop/util/TestShell.java | 88 + .../apache/hadoop/util/TestStringUtils.java | 121 + 114 files changed, 319738 insertions(+) create mode 100644 lib/commons-cli-2.0-SNAPSHOT.jar create mode 100644 lib/hsqldb-1.8.0.10.LICENSE.txt create mode 100644 lib/hsqldb-1.8.0.10.jar create mode 100644 lib/jdiff/hadoop_0.17.0.xml create mode 100644 lib/jdiff/hadoop_0.18.1.xml create mode 100644 lib/jdiff/hadoop_0.18.2.xml create mode 100644 lib/jdiff/hadoop_0.18.3.xml create mode 100644 lib/jdiff/hadoop_0.19.0.xml create mode 100644 lib/jdiff/hadoop_0.19.1.xml create mode 100644 lib/jdiff/hadoop_0.20.0.xml create mode 100644 lib/kfs-0.2.2.jar create mode 100644 lib/kfs-0.2.LICENSE.txt create mode 100644 src/test/org/apache/hadoop/cli/TestCLI.java create mode 100644 src/test/org/apache/hadoop/cli/testConf.xml create mode 100644 src/test/org/apache/hadoop/cli/testConf.xsl create mode 100644 src/test/org/apache/hadoop/cli/util/CLITestData.java create mode 100644 src/test/org/apache/hadoop/cli/util/CommandExecutor.java create mode 100644 src/test/org/apache/hadoop/cli/util/ComparatorBase.java create mode 100644 src/test/org/apache/hadoop/cli/util/ComparatorData.java create mode 100644 src/test/org/apache/hadoop/cli/util/ExactComparator.java create mode 100644 src/test/org/apache/hadoop/cli/util/RegexpAcrossOutputComparator.java create mode 100644 src/test/org/apache/hadoop/cli/util/RegexpComparator.java create mode 100644 src/test/org/apache/hadoop/cli/util/SubstringComparator.java create mode 100644 src/test/org/apache/hadoop/cli/util/TokenComparator.java create mode 100644 src/test/org/apache/hadoop/conf/TestConfiguration.java create mode 100644 src/test/org/apache/hadoop/conf/TestConfigurationSubclass.java create mode 100644 src/test/org/apache/hadoop/conf/TestGetInstances.java create mode 100644 src/test/org/apache/hadoop/conf/empty-configuration.xml create mode 100644 src/test/org/apache/hadoop/filecache/TestDistributedCache.java create mode 100644 src/test/org/apache/hadoop/fs/FileSystemContractBaseTest.java create mode 100644 src/test/org/apache/hadoop/fs/TestChecksumFileSystem.java create mode 100644 src/test/org/apache/hadoop/fs/TestDFVariations.java create mode 100644 src/test/org/apache/hadoop/fs/TestDU.java create mode 100644 src/test/org/apache/hadoop/fs/TestGetFileBlockLocations.java create mode 100644 src/test/org/apache/hadoop/fs/TestGlobExpander.java create mode 100644 src/test/org/apache/hadoop/fs/TestLocalDirAllocator.java create mode 100644 src/test/org/apache/hadoop/fs/TestLocalFileSystem.java create mode 100644 src/test/org/apache/hadoop/fs/TestLocalFileSystemPermission.java create mode 100644 src/test/org/apache/hadoop/fs/TestPath.java create mode 100644 src/test/org/apache/hadoop/fs/TestTrash.java create mode 100644 src/test/org/apache/hadoop/fs/TestTruncatedInputBug.java create mode 100644 src/test/org/apache/hadoop/fs/kfs/KFSEmulationImpl.java create mode 100644 src/test/org/apache/hadoop/fs/kfs/TestKosmosFileSystem.java create mode 100644 src/test/org/apache/hadoop/fs/loadGenerator/DataGenerator.java create mode 100644 src/test/org/apache/hadoop/fs/loadGenerator/LoadGenerator.java create mode 100644 src/test/org/apache/hadoop/fs/loadGenerator/StructureGenerator.java create mode 100644 src/test/org/apache/hadoop/fs/permission/TestFsPermission.java create mode 100644 src/test/org/apache/hadoop/fs/s3/InMemoryFileSystemStore.java create mode 100644 src/test/org/apache/hadoop/fs/s3/Jets3tS3FileSystemContractTest.java create mode 100644 src/test/org/apache/hadoop/fs/s3/S3FileSystemContractBaseTest.java create mode 100644 src/test/org/apache/hadoop/fs/s3/TestINode.java create mode 100644 src/test/org/apache/hadoop/fs/s3/TestInMemoryS3FileSystemContract.java create mode 100644 src/test/org/apache/hadoop/fs/s3/TestS3Credentials.java create mode 100644 src/test/org/apache/hadoop/fs/s3/TestS3FileSystem.java create mode 100644 src/test/org/apache/hadoop/fs/s3native/InMemoryNativeFileSystemStore.java create mode 100644 src/test/org/apache/hadoop/fs/s3native/Jets3tNativeS3FileSystemContractTest.java create mode 100644 src/test/org/apache/hadoop/fs/s3native/NativeS3FileSystemContractBaseTest.java create mode 100644 src/test/org/apache/hadoop/fs/s3native/TestInMemoryNativeS3FileSystemContract.java create mode 100644 src/test/org/apache/hadoop/http/TestGlobalFilter.java create mode 100644 src/test/org/apache/hadoop/http/TestServletFilter.java create mode 100644 src/test/org/apache/hadoop/io/RandomDatum.java create mode 100644 src/test/org/apache/hadoop/io/TestArrayFile.java create mode 100644 src/test/org/apache/hadoop/io/TestArrayWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestBloomMapFile.java create mode 100644 src/test/org/apache/hadoop/io/TestBytesWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestDefaultStringifier.java create mode 100644 src/test/org/apache/hadoop/io/TestEnumSetWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestGenericWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestMD5Hash.java create mode 100644 src/test/org/apache/hadoop/io/TestMapFile.java create mode 100644 src/test/org/apache/hadoop/io/TestMapWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestSequenceFileSerialization.java create mode 100644 src/test/org/apache/hadoop/io/TestSetFile.java create mode 100644 src/test/org/apache/hadoop/io/TestSortedMapWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestText.java create mode 100644 src/test/org/apache/hadoop/io/TestTextNonUTF8.java create mode 100644 src/test/org/apache/hadoop/io/TestUTF8.java create mode 100644 src/test/org/apache/hadoop/io/TestVersionedWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestWritable.java create mode 100644 src/test/org/apache/hadoop/io/TestWritableName.java create mode 100644 src/test/org/apache/hadoop/io/TestWritableUtils.java create mode 100644 src/test/org/apache/hadoop/io/compress/TestCodec.java create mode 100644 src/test/org/apache/hadoop/io/compress/TestCodecFactory.java create mode 100644 src/test/org/apache/hadoop/io/retry/TestRetryProxy.java create mode 100644 src/test/org/apache/hadoop/io/retry/UnreliableImplementation.java create mode 100644 src/test/org/apache/hadoop/io/retry/UnreliableInterface.java create mode 100644 src/test/org/apache/hadoop/io/serializer/TestWritableSerialization.java create mode 100644 src/test/org/apache/hadoop/ipc/TestIPC.java create mode 100644 src/test/org/apache/hadoop/ipc/TestIPCServerResponder.java create mode 100644 src/test/org/apache/hadoop/ipc/TestRPC.java create mode 100644 src/test/org/apache/hadoop/log/TestLogLevel.java create mode 100644 src/test/org/apache/hadoop/metrics/TestMetricsServlet.java create mode 100644 src/test/org/apache/hadoop/metrics/spi/TestOutputRecord.java create mode 100644 src/test/org/apache/hadoop/net/StaticMapping.java create mode 100644 src/test/org/apache/hadoop/net/TestDNS.java create mode 100644 src/test/org/apache/hadoop/net/TestScriptBasedMapping.java create mode 100644 src/test/org/apache/hadoop/net/TestSocketIOWithTimeout.java create mode 100644 src/test/org/apache/hadoop/record/FromCpp.java create mode 100644 src/test/org/apache/hadoop/record/RecordBench.java create mode 100644 src/test/org/apache/hadoop/record/TestBuffer.java create mode 100644 src/test/org/apache/hadoop/record/TestRecordIO.java create mode 100644 src/test/org/apache/hadoop/record/TestRecordVersioning.java create mode 100644 src/test/org/apache/hadoop/record/ToCpp.java create mode 100644 src/test/org/apache/hadoop/security/TestAccessControlList.java create mode 100644 src/test/org/apache/hadoop/security/TestAccessToken.java create mode 100644 src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java create mode 100644 src/test/org/apache/hadoop/security/authorize/TestConfiguredPolicy.java create mode 100644 src/test/org/apache/hadoop/test/CoreTestDriver.java create mode 100644 src/test/org/apache/hadoop/util/TestCyclicIteration.java create mode 100644 src/test/org/apache/hadoop/util/TestGenericsUtil.java create mode 100644 src/test/org/apache/hadoop/util/TestIndexedSort.java create mode 100644 src/test/org/apache/hadoop/util/TestProcfsBasedProcessTree.java create mode 100644 src/test/org/apache/hadoop/util/TestShell.java create mode 100644 src/test/org/apache/hadoop/util/TestStringUtils.java diff --git a/lib/commons-cli-2.0-SNAPSHOT.jar b/lib/commons-cli-2.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..0b1d51072a714cf1011e87628678a367b2b99edc GIT binary patch literal 258337 zcma%i1#le8vZa`rnVFfHnMO<&GfNgrmc`7>%*-r{nVFdxE%y4p`}Xhtw|l)i6Ft!p zlc&4uR99u@sVpT~P%s#vzdr6dS8V?}`S%Yr5EziWxT-L{w1NcV=NJ%>(r;5VAgJG8 zxBoCj`wjUoQ+Z(pX$f(FDucYlz5K+uoGd-VEW9i|&D6wng9_6E>&~ImgeZz5os`^^ zlp0VtF!|(zoJU(`gbJ#x3cw|cM)*wrA#x8M-X#Gp5}nOo_8!ffH6h|If;K`{#W@0< z?>_#K&7;k|?f3IQLS4x&_3Hn=yVdXGujl)_DHq!Ry(wV7|Fw5AXZ&Xi*ne9XIv5&T znEt^K^}ib$+uPdO+d2OMfcbw17~5F>0Yvc6AVXI}8+*GyV6pxY%kl>l(H~GoUoCA+ zOr8FKCi?^WFNzH9Ol&Oe{=_TcAFg2U;9~jTntb^m{Y(4@u#>5?{Z}XBKiwPu5Ad%p zmNtKAIO!jtzdOp(#PGi!{qLdkpRXqSKVE=Q*v84!(8L2^YGZ2bVrnApZfyE5A{dNq z44s{ml;mamg^+x3jSK3u=}#sD40;hy=A4U&@=J(B3V&XjNomAy)=P_jOQ!4%5d1+% zu8{8@N3apJ%4vVph5fXDe~JGC=>yC9M0_QxmU5Qq;WH76~Il2FgE8Klp5bkX=z6cdo|u2WNAi1o3+L%D|Ny(WA>ZJ2nTulgu7X7cG> zbZRcu8}7mXngrySgsmWffq*K1AOCBT`cI~*{+VeJLuX54B||4?Q>VYl4N{hu1!YF^ znXhSTTGQ@9^bZedPP{@ag`$BPQwfqmd`zr9$61w^;dpdAmA=_QypaqWwL>JKpWL0C z$nc1fyZg9*0nd%-g1K99t=%_BQQ|rDKW7!`Ud2IyNx5StTHs^w|70V8Jj;$E?PudV zZLkMJ2B5jQs65SNs&+eh1c30X-ti>9MkI6i*mKioCH=b>5xrQ{FMl5c~U%l zJ?x0$n%|xABB>1-HIH{DJffD_}RCx9H#MiC!2_yl>v zZ_5iIOWhWO9&Dgum&ri~rRvAb;u_nt$#ZQ8{TAQF}XAQzw`I**RI7Z{P6L(0?h^cQkCu zt&ufT>s-LD)sIrFQc#1(hX+3-wXcc*8XEn1R^>PWu5NQafg~i1I@)xI;7Mr0Voast zz+cyqz;Bh~UWLGOa$*R7q1<|zJ4X=mL*Q2||g^$y2;1j<_n{O(A= z9AEyPGNI>9Dij~Xq=uN4TxpHWePB4H&kEhxm;PlnZ_&b>@_sS}*yBLBoaEhpqvs5o zY~|ZZkZBH9Q!{&2C0q+^^_{a833%}FKC`jVgLqO2&mMtzq7rJG!H0p`Yz|XhgZ6r3 zGl|~f+N9`^2S66)^`B1Eq25Gg`{+L?~yn!_LyANnNAZ0KLWe+tBPrQQk_$lf1y}@Q~no zOIGyjq?s$69M%@MgIeVA@N!g%68|Q(zT6b`(eWy^t_?f9Ip?s-0$tay(qtX3I?I#(~-&Ns=iL*EZ2yqx`SKqbedW>-{^Kxxw%X{hts@k4AV?kjKJ;ik8?x@h;$aTkK#`X>;xO@`~x1AXFWrrTT7pC^|IC!h7L;_X9Q&TauKqUuc zgpvInrf2B}H*@($r;~o+8m-YMwS4{tfuZ!dD_C$RhUT+ySI_ZmdT;v7W4<(xaGm`f z{9Dq_R)WwLD&m&LjHbek9Y5<3S;cckaL>NKe?|E<3H%#V;T|E*Cp9&Wuv~u+(;$6o z?7X&c|3*U0qp8^wwxpBU8dj4a79IDc0xPe&*%lI)riZf{_Yt3?s5_rD2z?4~Jnqq5 zXE-S!3+V+kbwmPoSfXMMjOW7Tu0M3wD*;*=1=u!2CA5 z-t4mjS}_i#Z*X1uqcQ{BElXgmU>vtWMQc;ev^HBYOn+p0QUkUIP04{&a`OUxw>dlL zjwTGpJF|+<)=%Dn*iSKb4v+X@m*PqG0Q3Y)Nk2a_rPIw^$$5v)s=s%MPuHWh;DSHE z_IP%ou=#M8KZ;!VNjC60qA};VmOrMwbi~?Y0rs0^%=k4A;+Gjk*zv%5rmzb*9Lu({ z;mKP`Pd?w4?S$dG(l0}_cNQ3JNeQJb*cu1Bm_sxf${E8+vt@k?Fxa)fEMm?yPE)q| z0~R6Q&$KE>jcwUZ+>YxQVjgD=2sFA>`bdIkS6!LM>;yil#aWy0E?w~ngZaJlpPbBa zt0^&j#H=vanV50<5by>R21c0ca3z`iI_e-1cM`aQg!#ThW$zCA!sPUgV;#xUhPRYD z0=KNYU+-HnN6wBZM_333Hggk1(R8Lo0g$-_Xb7duAvaUNTt00;eSNS1ozZhUIJ zCV=bLjq)}(chrMVdcR*QfsA%5%$#aYDLjC1t*EfQrBU~vGOBB8ivDrCk$BMri49L zOd-)N8Ntvk7{M0llUxyWJU3j|0%kdStZ|l?{BFAxyQFB$#KkS%kV) ziEK=jetfvR3bXBC^>a^k@`)iS)EX(E_yOP!k@5NpYHzT1-SK|zO5-N3V;MlIuNiif z&i-()r&OvKL07V(z63ff{*=Mx!!!+V00YhGHMbqPq83{6!xMna+Il%K(<%+z$Llzl zPTpaw?xFgyjyS!JcF3@^$r^%ZH|fwbFBW)Kyi8L`oOH$K7e7yUxMHjBoww+3a7C+X z?1ULVSq6SBWSJZcbVak$pt_Yt$WvY~zWw%B0H8fm>sJN|1oVOYrvTs&v6<-K>4V(w z^x^N=EJ|b58DNU`%SS$aePiPac4bVRy3lqFh@>995ZH#?HmL|o{FbB57Ss|YL8ms9 z&AvVSaYg&`G|vU2)XzH=s1;r zA}Wo-PN9Fvvt7KRg2xS;!u<^#;xr9c|5y%SHn1V`fiI%_M}I`8&uBo(Eev8w+K-A( zPl;X}@EU4&y&q+ik^m63UgKBjO+iD6{7(ZiQ#)j(J!n>xm|0WPCDWYj+>W_a0lS$f zb2-JRpgqHYXmGX{BT1GrMHo6ln4yFMU;ez1A-Hc;0s;zdpGs_dgtZ4y!%gfc5vl87 zcw0-tB&bAGOPFEUAE^8N5>7n=ctr{627HB*kdG#TKksHNqs2q}$ATKcEUJe4;3Ud7 z#MrAeaQVz+7- z3hRi9_gjT;?THTjmvLc%HuIRZSFmO&^NU^lWn+pIaHNkgT1CNvF4hbWy}MeN_}S@u zO@)N-nzyuZ82Irh!oX3&P*(Hqt1w4_4(ic4CbgpyP<@OEgQ40JKa0N z)S68*j(V(e-nht+NYybYR2vKnB7!KD*P@^WpW85#krn*iwTdyCY6ah1L;5A?1=BYg z*A1wKVYbG6`CK07Wl`Gz?ciY7XEv9c8R#5;PPWW`4|w~0$$kl&xmwf?w$7jy@Z^mp z82IZQJ@9KwcS1somEcPp&)ZmkM0W2$y#X#kB^(9=M%ohqQ_(I7gXJ?;3cI)e{k;kO z?N+K~B#tzEPW_0sck!;X?(7YCwbgTc$mX+5Z(}eq-t5jWj+57IcA}_dcS7Q^i?&s5 zp8LcCY25-rb!nzcLe=+MTEPK@Z_Vn0w&Cm zaRKPNw00)ooZD1O;{=cPMCZC_o1*9;ATT^eom|&?X@mG!fg8{_rz`)fERfiNv1-p@ zCR|~yTC{OfY&7rOK{p^Fxv0@aFCbnk*);jeo_^$!jiXQ87&Ei5H9nCY#T$0Bve$J( zl2cn7m}AE`9XzE&wL1$*#^R~b2eHTp!3w&&&QNcuK_51sn4W^zy$M_AB)hj+rj`Nv z$j-VQc$%)!yYxd#$qsi+UP%uFtqBGCYqUuwTaCSSj=0B{;7#$waO}1!s^00+OmVfI zYd0z*9MkS-+|um2CJrSfzk)(NNqUh79S5)qCbK#V+5L68CD{PogC0l=HIaDqmG5@6 zeCtS>KSt8i_(%cbk%b(zc~1$E0Y^9-_05VnyLa46rc7$E$8d%@O%5p~UshV#Ic%?@ z5aZdbPqh4OJOozn#Ya@Wq$pvMtzJFhmclkWOO4GDr-?LeXabkDT_0 zjg$q>1sinp)}G5vCM$eT!zF7q@)2Y-*i^tWoi*uhNY4T~^Q1P_`~xnr>CrLnzEBNs zYes&n_g$CI1gJW1Y6jkCIwENHr*gZ9mr-rH!TZrhOQrpbE*{kY>J_P z^eJXUVVksPRlMbtVbBk}bnv@(N^h;tibk)W#_qa(Cv)HEC)WL;n~~qVxMq@;YwNS7 zG^X-`XzkVXD+eJ;jaV_ls_BlKrO4kQuax-=>lUiq`hS7`c`5ifrH7+%^+H--FzY&N9UDD}$k%>?g$ zzN(f>LF!1SBoHD;@+_>3oi*3f{C>y;Mh`|CsB6g`Dkw+&6Rnw!zoaMfVRcnMdRX)!VE^rZQ4Pyu{WiSU&!3WEEx{`sg-ikx5gu zS{#=Kj?#Pid2L-=Zs`&ftPPGL-Q^^ab`CC_RmG=ypxMF#B8fsj;kQ4%jc8aMT#uOK1I~eVPf3f)ZDeZaoo1@x4DhCw* z%%Y^-*Z-ZpDD^LLc&13d;9K0hS{V2}-K$E97K{#~1-nGh(UI~Ri_8^s{gh-I>BgT{ zo>~Vt`E3iBB`S*LBP}ZtQ&KIoNfEmjBrNmeEF#%0_jCx^C|(a_QH|xitEXDrkfQN+ z`X@Q>GhWP&GSaTsJHGy`AP4et)D=dvWm!X4&7%+Ws{7&)Lp8yVBNpOcP7~NPb_PCjYeB?8;!v zV~2_BlEFQHN&LJQ=IFm*KZzCSur`4m@X}`4PcUEH5Ggj7Tx~kczK-Suow%Wu;B&2@ z<4RFBY5iImw@}r_O?yoCSeb@5au~wC&TNA~7RCkkqN?t!M=B{bxV+B6cAY-qCXeAf zWz{CiE}5Kg zAjZ>Z&@=CIF<(wi9;20Wv)drMQXbNzzWkI?;K+)LO~`TOGwy44r%5VZII-wt>DHG| z2Ea^MDdc3`G7u0-GK6QgTxjdtL*G7|1hJ_7xFHh6Q1(@5eKEpGy=R^#?SJr1_Tz11T_0RK&Dq7u{isO1Wqah&KSaWk>l@Yt+LBgvw|>pW>>pbXc%kVg)N5FbMK= zpmg@Px<-n6z_Tjv`J&&0#|L|iol1H{{4kJTeu=eV+;0!0VS(;~Jj~(f;Rjkb_`!+* z078X)os-;4DT}4Lf+`G85eC+}WSox3TSAdCoj<+(I){?XCr|o%+`zil=^VB>SK32I zT1vZ`Sr}VprW#YscY&tm1gEEov-g3s6VPy;C)YyQ+L=e@m;{wcxkgE^d#}>T$c@Uj4(47*t?pdC<5cmTg zI6rh;;Oyto7~+H6MhCqfC>UmW-E$cBSoDSpomTRE$QtlC>@$XUexG{LCRNU^7wz$<%^-+4k7vn@Q z3$zgP^f}C$@Gt62bdCN}crmZ|$T@ckQpqB+uY7#_j=7I@vnpK%Bem`IHY0 zHCW6>I18i(qGAz4{E?=$S*|?2fBbfL-mBN+7AG%;J6#yNJ5obF)rq09E%yu9HiEOI zE79vb$Jecxcctf9E>l4@X9>wp3B9o)?Yq!9PYb2i+)7|8&pL{hn2%_h4a$Z z!Ts%91-GAmhG$U0x^E;Z{COZby_Vz37ZC+St8ef&YSW352nhYJg6q@9h;JO_9`0PV z!uJ9nHK;RR?XTkushOixkvx~<*5{*SW2$Kva2qO$G^ak|i!;Obauxb?cB> zE>CKz?op};)wEcy!XOqZ!>}$|YF}EqY+hTsvTOO;^0nh>xV^)b-fPOoUZ0{LY{hr! zgY;*%*LK&j_jKFwa92XgubabTpoyYH#EjcyO#J6Cwr`A+g6=mlbH5e>zTNGbKexec zJ?-Qa{lY}_CH`RKcb9zq_UL#|Yxy}D<<`~tzATWq!^v{5ljnQf-?Pup`Pm=v>%kD_ zQ?|R4aq{|b{2T1!ceZa1`;CaiT1X`Bp&NxSOsMuRQk0Uw+@oqgoCz#D?e0chzRTgx zp|QZy+!4zSio67kf?)`evt*tWNUL7t`8^;YCV5)aS7wpOE2Wps$=xfZg^Ym@>eOF? zsQVSeR>Z=YO96W>smDNuHPCiuS{LhX#Hq zv0=+o#s~Ht%G2U-aM-WRHCw#|CxK}XI!A@|s`BupZlPTgMxT0bRe0E&89L90!|Q0{ zCXe%(PBPkSZ>Od$Lw}!me|<`Zhu5&T)LU*eT3if_Kk7b#pI!SrF;KjGbzu=$X$Ylc zjaY89tJP3wCa<<^Gn&BEG3#%qZEj@KT^vYv9c(Y?|2S=S*Jw{sGJ(%9-aK8hSUwrJ z(<}$b%QaFgZF8kYU)jhHweRyvrZf$>vJFET(~YGYjGHdWjom1~J( zE;exqU@0<^pQtTnKt&IUVNPSfZYS@4x2-z}K570;)g+t6zT8HWA2PL(=b7tr6sTLB z3^P%*b)i~Y#jUuMgM+&nU2uIGw$>PW4N)hH2c31{Nj8D?RH=)+iY`|pnQrUH^Ypx6 zgbR#m2~E*!>1iFi@1>|tp3v+gU?9imr zmVVOD{$J49Y96gnL~h?Gl-oO4uvuPyMT%+-m5@w2tAs%-xAetnSKbpM+W^GR;sF@%g7csQ&q#y{`Oj+ z(7h+TMXr@O{_yJJPyZ#=q+&3hV=ihR=gRv>^fl35G@rrFhbT%`G{fR4sEVw&nWb%X zi6;5Cg((1prdD@^ z66G)Uu}5-z`QpXBz9|#B^ypR0NFg2SKgLMhIz7_%I%!|mX z(2Dcr)QSb&RU-o|PAn`SK)@Usk|`;Ita>>jHWmn^cbu-oQL)jI+M4_$1btryMDw^t zIdd$UNQEH{dE!*JEm2hKcdt6d^e%7s$$cLP`eO3P@5y8N0TA^4@OJGJxe(s|O3dX8 zG*dAAv%cNMaEJpC^YXmB#_G=s_|B_P@Dy{z3QU{>9Y%Wj8&wbjF^p2qeYxX~9p4k= zcD4A1Jt%+8;&{uAYt<-0G~Cq$o3MBmxF=_%!)Xg^v|1MR+J`RMwa?|6wU6auXp2$_ zR-EH5$XLT`OP<58{eYKnSjdg)#2t3n$d!+bT|2S*{=&}L#2PtiGd(km1*d1N6ym*2 zH_2XqOt3NKib#JHW7Au+FCI{ho)mKH2wJnZ4)KYw&3VGJ*4NT@-}YsC!iRHo*XAo= zA=h@{lp?HOiONJt4$ZF6F2FPJtf*Dk$7t(%rvqZk;5zrbEC)AkTLKgUUZqc76vCFW zn_-fVkO^<6fU?m{hj1C(_1C?M@n>q+xxHMGF&+=N_q?q z2L8?obae4Jd;e+P?gXaVmyWj(K$o#N73AK*VBK257KK@3`-Bl|y6-e=o36tXoYmIa zWpCm{#oh!C5A`-*NnWd38#mmL%~bgbx%rfn-APojwkpXXr+Nq<$%4T}#}Hqo_z|g< z+>2c~kEB|aKo4(332W~y0p{UwXCK|Dzua->m}_{+(kl5{f`SRc*rX+Iv$K55OZWkp zo}*oxcV@9b@k>=p5+7tfwSxRzHyYuY`5VSVz2Mq0fMsYvy(~P^Ivnqdx&Y>gTlBg7+x z!7W8}_bKX!9iTT1=1E$NdRbkwCZcP6*+X(P>ejd~vW)<8DAH`Jb5jk4I^;v~@fBVs zQ2Z6RTr6Q5akM~NzPXvv{pw1`$l*<`eFB1?Bz7@bd{^T*Z|>ecODj8$-0KCV{@SUyr$C|3Al-0%5MTp|!$?s@EWGkBw+J=F++&|h$Y@L%wNNVX1m zfK@w$yvX_doh zm7_2gjOZ~=`di~PaEevE(KtsLzdU0J*b!@w;jv%o?OMW_sroBUy4DVI&>|Ea(L@9& zeapX)dj@#-?Wt7^`LTLqttD{Krlentf$TMYf7kLQ@??k=-b%oN9GS$3)wu(ib^d-a z0c)b+OBAh3#Ce~sGX;E*;PIWy>AN=%W31qCl{rvY?Uw_PwwRwAf(WsS)R+_-!Rz-x zj{I4mCieP7ZU>Nili+vYTlX6KKrfBp2UDFwoozx0$Kdt6g0cR(MANs~HQD_n-{LT( zgc)K}?m*gT1!MbkiM9{l11;Q!m%`wp4WRO@mU_{pzl*Y(Bn)XWE9mq>HyAu;p zdxQ74lmz$lj&I{*!pDD)fI}lEs6z>H*HD7kNx*tEtiD;{3brqfn_U>Pf{9o|3Q8b7 zFHqNrp(zM$MFi#s3Xq;JmRVgY=`oczaM?5fU#HD*^--)%!m=|_up&-XESoq{bmXa1 zyq$d7V)|;E<||vQlwS#u{x%UWU?9y1r-(W#Y~GEkgAkBjFNV364_gI8wD6I9A_6X= zk?7|@B_ouY|5GbJ$KZA z09?Egjxs_3ew_;)BjXXZ0sU&F(5u1fKZ=-t16JIa0v2O*rUAOePo@52wW}E4Pw@mf z5gfln3CAVUuix^HbnnTOrCrFgXa}kolD~e4)eJs{0c+8dftT|H0c|D=YDOx zGX!TXfcya|{RFwPs4H|%V8F7cUaloNJg}U?cQE99)BDpVU8;-ogg`5eF`tNc8|HO60?;*fRkh&psf zL6lP@zm7F)OxDpcv+_|IYeDLaqzin^JB&)oy>%KoYf_;Wn5EJ0iuVL$Y>6Uw#b7@G zTb;6DSOt!=Q>t6qHCpL+?8xo*5uJsLSfjgB$xQSi62J5gfqaW!hI&398zSm?K%=YoD<$a<;shOsYlD$ zLL6B))XH@roi{>pShBaOS4`66EU)tDmLMdeJ({|D%X5E1%Vw-s)pOJg-PQ6yc4UeH zg}D5vQ`_66N=`#XM?q;r&?LKS6u+^QRjQyLFaLIL(~@T1G0C@Xd!$8tvET3tGcEn$ zm27WCQQd)^5@ta|o)?5Tt#d|0q-Q z-mC$2asV-AYYZVFe`yfg=p`WwQ45DAyOk3NA3x99&26aE4UVz_H}|E5%axKPDuvGz zSr?o{&;s}+&6(iR-kkMGUrzyiA&UL}m>xsscH}Is6`M}c)=?4z3%Z!mgF!kut zskt{{DMCvx_9V5){1NV|Ag_VfKbGr=_r6lY2#2A4`{9#UIiW>l^~#@Lz^VCz`2l_b zqRVLtJsB~v(oDO$51X+v_UofrrJhw)5^u+&xOg6t##Qe^?y{gc*yNI{R{;`UBJyd? zT7ac+4U8;y^w1%&!l-^&%u#c$V!V3#rQky1)?pjKSaMNk;TX>y%n#&=vONK;Cw}#=3 zen)RY^HE66X?}}k0uj|Wx~qjJ!=5Lw>;;4!aNquKvTr2%%h^MWP5sYRu5g4F!)2;( zFyKwc7SXQp7*^AOiYJh-7K|!APbfuADW?>d=Po_{7A*3V??|RrCOEls?H|j9 z{ff`06Eq(ScP*~8FdvI_MdS~&uf6WrIX)kem1|>;Bk1RRsem+n0MZ|7;ToWQCb@?J zK!CsS+;YA*#lCcg+v{M4Bi+Y$Ht^!PB;Lw9_++u}EIjoQLd#y*DLw93VxOS!;ZBxK zNC%Cp>1z4bysvfsvR84Vzy0(KabO@kv=IxuU2P@c;!me!*z2Hc{Nk|W8>0A2Bel3I z!s(JzGq)>baP946#y5uH67<2cq)v~r&_z<6W7GCHZWH^BC25qjv2O zr@qsMR%2!~rnd}Q?-+_}jIh5ll&rX7{XFw?ra*Tn(|5bz##v(o&y8Dwkn2NKzlE^c zkF-IymCv_2%4O#PaiYj;cZqDT!M9Brli7#G#fOH(ll$m&Ub$xahmj8l&I1(BBhJ9% zi_{4wX&K`Hf)6smQi?&2Z<_6@I@m4GXY!>>hJ2lxjWwal6PdVar4Q6LU}yGFV1F}g6Y^~J)-u(8us76*bkaq zjXyrRGSlwR+$VnRL+5CQc^B#`?~K&-7THg--;swMo?J?@*AFBlMt+2HtS@1yE4)BX zlo-RyJgF84e&V;Wh*CVs3e_JX;0w1ZcMt6b2j>}1-wHzM!cFpIEPp(2xZ+=RizcMe zN!kH1#e88ukPKgsd7&UnQrVZ(gai;&?)bub|GiL%pyiQlfC2;r`CFR$XWi-#72@)i z&d!#0=E6?qUu{k8T>dW=;(rw}H&pBCRV?~%@j+p{`$0@6`G;n!Bhz%1Fx!xJNK3m8 z_8k7Zgt?*OcZIl|!QIhhC#R@@!1fj}+^k}qzrOz^7;oTITU{{@s%uQWNB13?qrNqM47)2U+-hQf5~JY6jw z+(4ShR%YVb`=IG_mG!eTty6K+Onl^cl@Ox)rI%@nK$ktMnujLKp$o=)Y>qJoqe_$k zK{q0}WU8w|#Mc%?$k5*f;-cOj7{dtW#z&;Syp$z1xtJ2jDAAVV1WwyP){#%$9ko;F zjTWGrOV$mGHa@_A352e#QePqdp9SKq|EyL2o9Mqq>i>i0afN;+q)d3??@Cmv{;-r_ zQhZ_`z~Ws*TtOkK3mw*#3siL#T;lH-lw=6rK!ianSOef(2D0a?w-=FzEk9Eq+yGL~h&q;iE(=GB_l@@4LBD-Fu{XJVY(d}+eZF54@8k}%?%Rxg37tMT zdB%Pipb{BlhrM7pf2tj@I0poob_)y9o`BUXGBrH?i((`jfEUPbii`g$_WrMpLjPHV z|Igbc82{$>-wPm`mM*x9=$~6s^?`hM!N%qd%sNVB!@)vYaz;%0DBq0&F3IFU98E0g zXyeH9$jK6uixQiKwASiV>Cr6oWMIAzagdV-($neX@+KxHi=2wU@|PSxNO~bWY;hLE z(Zx`4kFFnWUi-Rv{Nz78M9krW%7y>svJbbhF9G?~>adB-)|QfC&*hkdi@wsI@V4m= zw0$t!(weq+w7$VN(zor{9~0uYPvhz z_9DQkcykQO@a{*c=;RzLoNa^=;{L!^!{1+EuIC<|l5?EY&*#}2hUsyKqNwk>r%ma5 zBEh@}OO4igbR+>rNq>HfX0q(b2?9TPxdYblip=;N7l>Mbc}b%MR3Mfl#_1XYhS9eSz3p z&x%CaJ~N=pdz6(87A;qpd!26 zb+7ah@(jjm^KuJf=N{#{r3J15&zoF|;GQ#TdM_fnFFz(ymShPDhI1Qk4gLi6xEChz@zy^>!GF zdp4;h3KXwaijmTnzIKAMWw@cu;SoE!50~U% zo2GblBb6o1Qr>phvf4Yux|+-Fk)56AOob7Aj0l$6Erf5y9v_$**mJ+Ooo|RO4TM8? zIB}(lBQQjkJ7Dno7V`TbI4Xp5n2n7H>o`1k=Qo2wYlq1hDGesB+PapIwD`O8wl)-K zbk3U-`u8|K**A%)YaD_r{0qWULM#HUW%i9O^+EK`OXpVI!F6}Z!JO2wj^~DO#`=?v zR%ruEH>x-(GaV{SZIj}r=7G_@7urvWpMbJywT+SBo{%1Zz7Av{ zNo|pMj}?!$>O1H);H-HD`X1H#Oq=K75D_dfIYMq8)25uTx>jbd;Ys}LpK|18P}I|+d-#IF@sQ;qGTv*Rb9I2 zEWpzD@61^gdaO@!7b*e`X&V+pT-NYgerobm&OS$1N~b<4e9;oWZ#X4H2jafs z@T38)zh*tMj`XdnBVh8RVX7}dA@(Y#sV})B@}fbgCyamVEOf>?x9t9IcH9e$`zZPR z-nYa9dt18E9;eOumRVaQ4(DW^Gt3;@&y4Y_K!DV4X5DAi@FuE3+q zYG{_Wrm7Ye!Q^z$s(^cmGAFIdB-*kk;^#6VoN2t7x)OPkY(W2Nk4H@L>b^V+02bL>a_ph44_$u zEB~4PKywh%Q7E%`1JEbe!-o*!e0;B3Y9YMB9NG9N-5B2Yp-@UXU9SxzsX_)P~Vi7iNxHIV-zuHRv;#Goa~Z)O`LbQtWES$K+LuIalFp4)s}e)Unm z*LZGQ#0-vHW1?v+AGt8fWAc-(4L+G-n6Wr3-d-t7w4N~-we9Ijy9#Y*vKY$}ay2b^ zeSB!9Zt#dUX`hyL!9|f~1(fwy>`HMmv*0>bi5o{JP}GH<>U)OAJ6ep(fhcJb0`3iHF=`I!DHBOx~S}gn_AJ?_c-X# zS_-7)FuEAi<*(W2`^9@$GY;p2=J&1c)#;uJ3KE}uFnqzf4YKnbRR;6!>Tg7u7UGD0 zeZ$W11cK#Uq)#8*EAjr+qNB!A8fHYRO9>V9Hy$wN624%B=~`YmEP@0{(0G=N%X>7O z8@tXBXdfAsPJ-KH3u5I0@->B+Jl95alDk-c`az0~Hwb5wCEw{OXysicPb+<3m67^- z@&Qs}>T)iM7YN`JkM_Zd7+~d6;EUk6#Z$nX-lseB8jLuCJr|?sb8jJ+fPZ zDKx@dop)H_TS8r;PwTaUxvYLj`MeXC9ojh-Bdax5i=jP;`D}a%i08C3ehZo#$~{D^ zD?wojAfLR6Cp$W6kij5{!Ki{HtLMb@e6pL2@Q+}lUP1HYq&6+Ol6jKi18A4o{wX)8 z&v=qbx;i>%&N%?EIgpNB*T56e zDMLQScrmFvP0(@75Vv$lfQP#DheULhmiK@mR{7;eHj;pm4cCIdCM~j0`q23E(O*#I~V!QaLE1l5*4! zhgH!{+>1v`Df$H|>`nE!N>mL6mXP+~LXm-K5jBl-9HluYr8q2c-<*_vg&8WB-WF(U zdaq6)Z`_|vz@btsG{P~BPPd+khGY)*;6lu9xJC*9eR7GG6b1QwFRDD1z)wqMNA(Cx zSN4;LKN{e%CxbPcnwWD`5+WUSB+cQpWm%%EN*vq~QRhkFoP#E@i!{GDO^stmVKt+R zlP+R8)uoGTG$Mt05bBjtNaxT3>8NT<+Ws9`UlJG#1*A#0a}HKDa#!~8Tgs8Cg{B3@ z;j!At`tpW+zDEwqo!LT2e2#$A3Zj~qNtl$misw|Txm6ijTHcFz*PVRFCM^j^idur3 z!RH4wRE2vP+%COa7O~N~<$L>7%^AsU{U}Xiw;|1`BOf%FMEiPnL^4qhfsG|QSH$P+Nn|vgX zJBfA?=&_a{SIW&Xt}4J^3?rtQ65cS%kYkg~*Wtr+a2 zXYlyVj55mZq&4iE$8NVdI;Q9uEa$8>VA^G4kRSi&VNK3HEPjnahL8q*3gb*?qp6-` zk(ZHMIXpKD`3!~5fU#nWhaO|f7DbG+y=;_Mb>|Mrd#PMQ4r~ZU6rT&cSuA7`^Mmu) zunc4owbeb2gm&YM(P{lHDW?7?1Y}`zx<^y&mxmOHU!j!XrN{()Rse$mQ6IBsY*CL|uTr z=O`2}dLV^PA8@K#4-mf*LHAO-$McKIH06(!}+>oTyy51=8X%fyDPYrr1Jthojnr!aW z250-@~3>}x08>+?BTI&9lU7>^xBpRC}&-^z@TbfOwjFf zuxhtfejl}LS}q%Ws4z7(=#$JM6%a>-`S~ae_jV`}nM?u=lc##DQ^(Q>cB(!ZXd`>)Knf()|rF~Y6tGpF*-J_-yp*&-oJAhK1agBGR zD8yM+VYm@z$1(K9jPkVM=Vy<(%2)+%sr5xQ5L@TcZ;`>076kUTT0-ZFec!9Po29`9 z(9(mNz%Cj=pYW$mZf+$tIks3Kw?UGJy~1?GaMfxLqLUx249_T7J z8q)GtP>cQ0Ei=b*BmYF&qG5c}H-%>#h5rdEq8zW4@oEcuMYp#W7Kt=0cvQUt~l`t4-Th!RYKqhn9TCp z{-NNO;tcdkQW0}&Ya}O@?HL48Q_z&sc{Jgn(fcO>QzxN_(X0YD|AVu4V6v=j7PPy| z?6TEm+ctOEwr!)!wr$(CZQHilQ_p#4B2G+v=lLS$7wm|&?!7Ya%qy?l^PdvKFck6H z>%}Ydem|INfZP#$Ho9!=>Rf8>%AD{1`}zs@!{gQtxt$JsD3uHdJxZ5!N~9K)yb(vP zzXWrt)PH%Q_gpSxSQUL=uLEIF9mXO>_P!v9b&(4tFv{%E8-HxKQdx6=7}lnF4@cEJ z1(L8*PuXA5)&Y~jJZo9kq6K>>k**F1fmNqL%Vb~-+EP-h3AMto)YK%txU!Ec@)Jm% z-`J>)%mg%|PH@SdVYD{SgcHN*YP6hqUEx??{Wp^?r}J$HAE3fR;J0gEW+KqJLKC4V zpcUgZQiD;gv}U6rF|&qCh$`{Cx!YzQ1@M-N7YFLRkSk0uqj=C(`g{nwmz(?P9;J65dN(Z z)zGeD=y0l-MDi(5aCyECHM=z0rh2Qb4(lxLzA*^IV4f#JOSP$%)-qn%ah(1lMPrwa zO(8AafE%`D*en@$*56sqy`#NqOHaKbuUtCczFY^`%N4g5(j0wAO ztE0n|gG13=R;t`<+P!jb!&t~sL_(CB7jUo?i6iWCsS{09X~Mqh0)k+3l>Z6zz822) zu2+^0#C}i|oUP(;a0_|OW9X$ZUo~G&v&yh2`p(pX9e^5$o5Tf;Jf-+Nxq=qW!}^Fp z!PtX5z1}`Hayq$%8yQgfbZXP!$eI@7C>*D&-%u&bC3R-oJC?NI0FlVtO^}?nP$EDH zD2^ggBU1~i@YLo|G?S^2+l=(9hOEy|2(wQ;1T-32U2*RwTJ7~`3%vHIvfA#h#zA0G4}@#Y#UGfVxQ^;=t-qU`n4J8 ze7_q%MYRv}E0AQryTWg67;eHFudMz;+gH9E&F+sb5N&QXP}}x4a2k-7fGY;|!8-U^ z+?=^Mj;Cfv6Nh(+*#~}yBrS?hdSE#fI0l|u01I<%)d`~dwXT*X^)6OIqVp*0`v z#F56LQ0#JLe1ZpMOHX+{S`;0DVPHq72%-p+kuAe%U`xane=`~u=#N0^{LX_<1P&yT zWP7oPH=I>EH#97yp?alxWgk7u@~h@$e_^Dc)8LPHg}#;D_8O^iD%|2B-*n_b>ZWrR zmVJe;%ITE5W6Lo#%h6=E&}urr>eT_87reRVlztc6TW-`_U_ze<=;-Pe)y}(&IbHMS*9lwmJlYi z4zOhmz(?VS381HX3_))0Lr;8b3i~lc^br-X4Qrq|2tp#*g4TyDXe*%F*EzfsO`3#} z5YcH)$8->2M3um*40BAbLq9r~QtxC<;~+ZnnJZ@poi_kKy(-N!7_Lc+PQJOdQd21} z!76NCdjhqtepIA-Salh}n6X`99dU*r2;gd-Vs#rkQ08eo;OHBR1`zOc_Vh43K)%(% z{Os((kvQ@Y7wOh=BrayGf;szCY1;)Qt)Y<6eiDXM*-IUOhALuQz*%ENj+X^Eug*rY z<6D?l;d7HYn3{?JAm%aWQf6a%D}uBi=p8fZM$(F6kB!$xlUZTLxr=&d)Bz4f9Zklv zd&+W;p{uwIB9r7;wLGxT^rVBp`hfP}5eXn1%%fW|K>?C0;^Y)-4H`6Ma%W`Gr;zii zUF<+8{Ni1^#ocb>0eE)3_Le&9lPhwC#vg60MS6#uel9kq9Q>*5m(1+xIX^+&YE4zk zVSs(ouQ3!F@tHcE(`LJ6`m4MWy4k%^MMF52lNhBqfB8_45*=8Xar`un!W2RfGRwMH zRl-bCMGCqP;H0UVlbHi~CDXM(BhoNSCYTfhkMq!NZ@9^gG@M~<+9eRLgz6y{5)Yn@ z`XwNKjA!^5@16buWyGrHE8`ZuThI;kSLmH5f!Y!8_sz$T-eV64<{(P8Wo_RzRnc&$ z2JQu)RsOV(spF4$oX>1t-GXidLp)fGlh=8A0auO^JFjB{&hD;kTU>&oo*yV|VjV%U z(Fhc7398Y0i1R7AF1Yooeb{3S-u@XXI&oZSGr{960*O<5EF1{=DU2^%+4)3xF*M!)vmEP zx1e`oC;!>tCJ_`622@cZhV9C2b(f{{Qfq+(K8FB#D??qU@kT%0;_1s5)P{DhaqvIrP5jto>x(ew@H_hEj%U$Pvybuv6K4rsa|Z`OPm{AfVtk zN_90x)?_wi$IFe4k%zl{jhihQY4vHxZ zlGV5*B5%j}M5`WyIMU7_@5>I29d&9I;O~_U0Xxs8*}#ksOqfZ4b~_lF)Pw;j3|~zv zjVM?+x!8QQs0A9KY@}HRyUg{ZD4`SLs)o)G*lu`-%GH~D^Avn@`zl3>YneV63)Lu= zjH;g!jZLVlEI(5e>=l;y)7v*>eKCxkfS-2>fp*m=L?;D;j$(W>SB!RWFAz^EJ2%hU zj@YHceLZgVfZNqh@gvMt>1=U%nQs_1XKoyTX?KKSyXkHaY0zJYwNY0Z^4h_?19h;_ z`{jVqV&`tKfV&5;lk7N+5uUE;$0l_2J_)HldwYn%-U#KvX!eD_31k_tuZU$Bu84ii zDs(afxqc;JK#TDX$9YFoH-x1YH#Cri#&D9p13}qdE2%pH#j*QB5A4Tc4~#tW+X*?x zF-hF84a7@cF$hcb1_jeux*hn9vpWLH*~y+u4JU$YszZwI@F1*cl6>JpbB%+O-XlII z3PEl;&E>oBMz=H<&s=#Df7)2M(GlNda=6^yg&t&cbUWE)fMqQo0vAuB(*~=i}DA|X+xa%!g*$T z>c{6&L8xe4ba0mETtNKvru{Z#Z-3kpcZ0(YDdY-*MnH&Gu#Gvw7bV2P01M)}_(Iih zRnALSr8MkZC+^QU5b>Wdx#^}Aq$xap8&T1F-tGp9KTMy0aD(y$MN{4|_=2CX_i4Os_=~v^U>j)IX#rp$J5LNji0<3*mXBc@7XbG{gCGyfttjzN#cjb#~DSW5QV&JUl1w}h~FBS@$&FT zIH|+!{9ZLq&mRj$K*oF^mpgArZGfqF!4-L%IiY-vy3zJUsnsag?P z%a#(_?E@iZxQ#uK;1zy2Z<+{thPlchQD=sG&eq!i7+=i6#o^pp}3Z#{-8l2%!`W`htD1j54dVL0@wLwZ~QjcwOXhHQJC z?dN1vx7M+1y2=KrorKm@EKBmJ?FR+Vz!Vsnm5W_ezxn*}{!$lJc;C7k83IO=%g7V2 z_!pYoN3)QoM(tUkyJ})lkI9W%pqGT+GqYZ{XtY4QP(zO}$VWAkQ?C!i-6lr~4_w)_ z{~NX>s1qN5tpbkb`B=P_)GcD(l;H{^hVmfk3u`jNYXL*+JI7Vg?0R;DDA-tt)X-`y z!=8K;jB@na7<%4k%|>#pX&EenfUKjuxRaj$$&m7_e+z^`@ku3Ng9ZYW{M396QLdVq z$<=?U3_?|ppyj{qJ)7|V7NaTuXK?1fCh-3+U?%e0TnYT`F{n7Ns#;tM+fo{!HTC8z zM4O!_5GZ_JMsJGUEFjXjwQ)n_dIA2Bn`IF>4$J-CpM($4D z1R4izAjOTi!aa(25^E^l6tlmEr2!*D3Upi8z6`Xvq3Ts%Qna=`+hACf%_)2s&|&sQ z%TQ%P0)c%zg%)>IV60A|qBI3oDnV#73hwn-@X*0B^$T=PBA8G3{FzkeH&%O~%G(;# z5odQZcx0D9dO48j-!X&m>*QQ_??451NPV&CB=W^8A0rWd6I-pi%|WRq-2>v0-4eF}_;~ z0)d4HkgAXK(3;#zD`}duT2xj7SS_q7hyHf3GQ!Y_m*{lAcD+w^K5sZozfLG8%B@W6B92gt!EyX1@VE7#m0Jsr%gBvz>np%sKbg))&G{N;kFo_2R6RD6ziZ*` zsRiQQAKm`>QdHvuxcb`!vps7o17En36ug-?@jbpj@4@V;(uJ%Y%AL2r_1k@xl;GLy z7?@vCFPaJ$eitrGc^hsIA3J6jT)(d$)4OCB0Tvv^K7CyS zxoCN?Vo8F~nyEQ8akBR5NtH=%6&q58hQLZl;DKCgb#A^_zF8$b zi%1J%SvWIRtWyw5lAHakL31Fv^PO&61yeGdkZw5dV-0dH|M(2iaDVuZDWSMYHQ~j#r_3RB{;0pLGNk2Vh zrRqk*MNKN3Hje-8145wPsxZ0bi|zMGnKXh>p)%=9cUvz{?}jsJy(=bsg)!l z3sh0^blKFP&Z0?6CZr6Iixp!=iqM=Lxb-5V6?$Br+Y&0Zq>bDVdZD&eNg`$AAKI#1 zA@?cLt;Qyry9F>-n4EdeEt`_V5hW7^xCEif1uq>M5n|x|n`i+3iBU+5EfKD$w<-VAJOPzn-LBeVD2o=mTL(?zk$00HC^P{X(9O|7bVP;`dwYObXB1^2;=hP z@fGB_44JSUw${xDIrapRWmIkucM&sE#~j_$W4q{DIF|I7)5H7aC+;@B+Z{9ua>4F6 z7X0SqNoKB!!{08KVbNMmy|bIx5(E0AnjKuL=%_{2{9Q;K-XR&L55?i>8wXgKvlq~; zxm%`RN%f$AD~(nj)K`I<4ZquNy&kI|%c`9Tl?)I-y51Q7pAv zbqV2$Z|}Wm$UKI@iXlCdyi@37NX(mSSPGGS_uHf5uz@dES2r&(MK&yzl=t<6T(7wp zc&piDfj}xeFtLEMevBVo0?XgLBqa7^_OrU{2}!vkwcN91&B2|DcXLz1*14N9V4iMj!ZAO}ThCB}pfPm<|89Ion{ z-!z>?YaAunR``oZqgT+~lI#tEOS5U4U{cHf&7-!UBE-gTEh99B79yPgoD5-mNyJMQ zoeO(`c(#wvf(b$@weL|Hw~V&JCn21_tsAiFYU~Gc_uO==DRLh$a)X@Lwh{>`T-{6U z^N~04#r%!NK4$XN@(pb8UplWS+di;|3#*RGxnR^l zMgAEkyi9W#Ebig3504?~>mViXB1nHB5%nw`X9{r5o$4Jf9g0QEOg|)GrXLrs{kxkdv(sTf!>|!< z52Oi=D5%|l)>>%Pd2>++{nWp_0B%M5(OT>=4tM9k3-nl6=nlvTi+5M%8QA50 z;%QJW3bWSDQV1jofgZuj z?LXpUfX(cpjYF6s(2i~ts~H9$B2nC0YxOeF46}_@IcNB+&4_5E^YM2%vJ-6q{f<0t zButRjFzt+mNGU`@B2s0yv6ocELu$hg$*Vr5S5$7G%~zh8NCjx_z%6abQ-1NpYVdi% zc|(p)tbIYoxP_CMQSmUCeC+1i8**;oU>#!JY6F7o zp+QfK>7n$L@Mc;6X16mfrdv?gUtqR0-9dMI@`=G#=HE%V0rp7p+=0XEW&{L&+N8%C zLg7Ix7Y^ZzwnP+xG)3~%Y83Q|?Pw#;spQ7)pXtBQcPhVA7urXtolNjyZ=dp2cbzkK z=ZGr;-sQ$JD9dTjMO%9~i!u}gcQ_rqrh6>jnEmVEmU?c&%(rHnD9d)b&# z;ul~t@}zsQmBKKUvZ|c-Wx=p;7EBR~^q^@RAGA^ySP$Jxdp%Lx-ze~wJ2ph{re8DFy7dd$GGr#(*#HN^E_33VrNQ`e>ji~775hV!&A-ajY~Nulp<5AF=Z>2 z^OP(5~_G6)gsD>fM`(eyT;rZ!Bp@^ib^HD8Mb>5U3&JK6H0%;Q>DPsD8dJv|f5^30Zi7xG~H zTBsfMFSI0Y#`KjowfI#x8V9ELhxdPy)1h>+AClkTNe1e_W&8C1mhJzev9bRD;!l*% z9gvie|60Wx&pYOtR|KK1&j|<{iYFV>PbN)@lw3apqx3fa zF3OX~i%^KGbu{#xV9U>+V%rz}i}9GzE}ots@hCXOee5#L{mf_ z1V(F!9uDdz2K<*0H-vsgNRxpi49UJ^6eE4=K&q-Fq9pFq4v6$v%1sWk0TtIC{)-$O zZvVbymhK%q_4-hC*t zzN2e*D7~ZWaA=)jEB3Yuq|hg)y4~d2-`^_ND-%&daCjkjQCBfC5{xs?oNG&nqVw|E z(HX{2DIv?tV~z8@F3&_|ozLW3eGk$2_-s;$lKI~L4!+{9;$kX298ZZ{Pv??js-r_T zUJZ5s$m2XbeD(51tq>ga8n#URvhJ)AUevFWYJ-@#2B`0HXtdqc4J#ES;3hzus{VqN-Tz> zM9h6-32lm9+$&{GSCe^P{Rj$j{ga*KLY{f?Gu@y`bVDOfy4xD^&UHz4_0f4G@r5vf zz#xO-oTYIea&tlCWQXopS6_3Sml^Gk<;t^IiP{=cvKB5R7HZ_dTP+z|tCYZHUnFk- z83ST%>{Ps-)Cy{v#^|+i+jLc9@wQls-lTPS*kGCBw6al0@s1ZZ$#Zg>kicVyL^OVa zvze$B@jR0x=&`MKFO6qUfkJQPN|7#Q{CdiTZe6B0aQu4eM(=^NWxO4KZ?M=LG3!*G zpSWj14w{FNrs54fkK7G;JRn8>27^cC2DD4jmR0}?lmGO-Zu#-TsWa0I6mGA^?+7&A zSsdj;^UpeCUQXccA<%Jd)p4$}Em}VD{Tcx^lsbksweDt#4&5Bonybd>rt%C8)h6DU za5}Q%MERW1a(L+018)#L62N-h7<-DGzwqJ7t1NIG~gp+`7YkMU$8){AS&X)c&K=?JK!zO24iWK*CM71 zaZI+?Usw}VCRNpDyked4{Cj{uM$Klr!o_fjbMWj4-#37SPi)d{Zi=gvSf-6q#(Xk< z?XKX7tCUdG9zj$Fbe1O0uR)>#6|AbyDKG`-Vv6kcXj2G941% zWp@oy{Nmp-D(+aI$0XMNJ7FQ~L~L+&=b$Q*n)N%#d+H#~5iNgql914lHAfqmxQ`X! zl;OGgx;Ek9aQ`H4B_ztqh0>Mf_KI%XdY>p(1()y{BsluZdt}HXklG*}JVIU!f`HwU z1>FisTY}dHS2$Qc3>oP&GS)WATZ%KuX^LOuq{KnUbBQm=QHfU+v&EI_;e?RL8QdY>`brp?S#u=$+ejg5P(u0mU1a#p(}&;X z+8eoTuru5PnMu)bjF^J%sB)43rvxY(<&bC-~@@0}moBHOAX&L~#e{R_!vcqhKTqryK z;*M2~;%bFCNdAJpt-H_T`#yJ5P!oHrVd~x-zl!D1Al@YKeTNk%HS5&vOs#PcsZ3dZ}(3Ef&O%{Vlmd7}# zP$Xo1`ol|?gKrlUJ!ls)h!j^h$X5>?7h-!=Yt*`oh}LC#%j3#r>UC=RiuUhwe)f-* zNVL6?9WfZuebe4p=ZC!%D!hc{ccHW>hg5`9K|4?MXO|s9wymGQYHIfM6HUDl`A80R z2S(&0Mt}k&D_6cO;h(9=g~-8?ps~_pdn0>cU1ZleFYl7_2#8e;V|TpiKB?aP!(EZ}@7?VnQH8Ak=$>iZp3!EEn28 zu#_9R^+Q#jZskL*m2g{7RNyvC90|GcSa2waG>v2qv}7`diNX%YJ=-eWL!nMSXiiS> zuQy8LF<~z|no1x69BryhU9(1Ihvt^=ZAf%WO%v{bgSm$(4xgo-SymNKZbETb0dCcN zdqc=;u%tR!qz(_ZR`ZIQ*-i8QZ&~>*nX(Rz_kek{?-ZtO{yo)jq^Od zj%V|tDF#c!_9Ej{;%VL{7(Q}@JYSV$XifH>4leA-mk}DY7K=JSfPm}QMix|u`Z9=v zI^X_k-M)LSG9c=q0S%@Zx2~?lZiR-E{oWezp;J#_(GDs%OuYigPD8GU;<=ccjo#$o ziR$D4yZMD9NO0N9X~0&zkHm)8PwK=KBr|x^5+$<_i6gT|j@dS+<-WXKz0c^XK5TLu z5jDMg&(ZaV_Ur|*OH#${1;wo$+<=4UPbhj<*}k@1_CP=saGe+HR&l;x_25lKRCRc* z&B&dU=FBa2SK&TEmsDL6lsB^7pRyy{xB? zrQ@7gi7$W(j$t7o6~Zzj_B6M#xCb+}j=(_0T!ovveU$ELt@bH@bB0s@Min4B@{#2{ zL)pOj32MUqV;};xKdUHNC?bt?TqJhFs_W?_hcWB2z9PnY%qqUKytrBU^C2D^#?-Y<53r#xaB!4DFuX!VQS`nA=}UF8}TOhva(rZAI0bJIMt(P zi7xis&SXIk#)0TgUp=>lnnJ%$SaQ0{ds-un!K@uq3s6ohQ6Q$1fmEGY2-9yMp0 z-u4e(1Fvr1$QSm%j6S_pp7AE#MHb!okllyDw=O9pr{g~-n1E7x-^EivY-I@leCqwU zC3*6r`9uQjPE+3O!FI3>337QS33iC46e#n&hc0g&tDFu=lGzx%P!cS$Dy?`TKGu!G z%0o}sfq#v{{Do$%{hlo4g}LwUg4O`O34;Fgh9%R9gEG^jAw8cevFoaxtQ8B_>R-!A zT4IQF%x^SeohxptMDVYyJmi{)!n4xZV{YvUZ|O$7Sm#nAQZa}&?RNQ zd;yJk>kZWutP-JyqKz%5) zY5`ac<1hMmtoec~VZxR2WGH>Q%3ty)zHO^`V0k6=&9j|6OThU|UJ=X4MO_@*g`gHZ zp<#K_u)Ju(yhiMRc8eMn;5~7tzx|gcdKp6681B16mG%D`WEI_Pjl}-hPxpV;O9d)h z{c~0IW=XKF(<*F0MxJTntF=LdN3r~A27s;RX7yX>(To-iip~e=sxFpgM#PD#eD~$H+zyWHR_r!Z!_B0LerktCh112a*3X+CAq6p6? zAA}+RrNEG-QUle`Uogl>2&Tc0IvlWKhe7JcM!v5IPfNxID>Z}xdVZ@9Z(`I@pi8Ko zbQ>1o0EG$SsWjvUS;z@9UgILJD!)xflv_ZUI73Voj-IrYE){ih`qOIQKth-ZWqAUh z+A5^AT~$9C3j-31p!7jiHGOVx-<>T>fT-b`a{M?HAnU+?Xh>{HB_O|GEWUwhgt~;1b|JN@Ra%jF!iBJ0Y)NR; zcm(aFfuT`vDTw3&4VC(B7E86dZOIya2?VlJ5SX#G9B`U`-$>Wqc+bcXtKav$?5M0# zEh*Bo~9AZ2x?DZk=Mg#OopR3;8{XbVAm|kACO;l+%O`4 z#wn8$kR9w_86eb%5k`s56CSdlpwSHuTOR!05~0|EFH72{*g-y{5K`g_$gzGXan;B{ z2UF}IoN3obABYKi2082ZM!C)p9#yNb{=QY(LE?$2s-f$9I6EpPXuReSzllX=mSHeg z2mvC);BI5XqV3d%NGW&*QBbmX@41@x!1)CBfk^X>qll8tc8YD2mP=gU3k5TSC$l+g8cf9a8muSOEC7wnx2$b!^QGs!gw?s~%z8Ov9fo@5!de zhQ;HWMiW>@e3+72-><8!PSM~s0{_g6r^bKk8LsnNy`~-}e=ihQ=UHahImypM8V2>Z zlvP{?*2^xqR&cQ3jX%qc^!LaE&_Ae>Qlqq~OOFSUI8$pMiK45rin5iq5Z0#s>eZav zk`BZv7K@4;De-gNVnq&U70*B~6|IH)!ror(6H2HA#8iq0CJxTaC`3k}njKWS-6~$jU=1%sZ^~bMX9^-)9}BL#qt2QFb=3k3VRM_`>w zcJQJvKFE-!bUE*SSr2|$LX^&I{Pp)oeo4v(kP1dDMdOGV%01Y$!u2(MBY=$vS=tiV zk5vRLwr)Lk_l=$`Yqm7P;jl4 zajm6mwhE_R%SCeXtZ8BfNLPxEk1rK28k-f0%}1-A=U0yBTcwq+U~f)mdk6Zbj6q97aKMk&$0G#)h#%BKECMQ~y)lII6Y{4^=gsefp%mur`T;N64Od;aC}>wQ zU4b4V|8Y;HJ($f?D>u<3sJf)&B-W-@{WZ_bRuesONmV9xonA;!c# zFxI7mNI%$ON_%`ttg=GZ2pR_fHRHg1TWs<{T!p2b_-wea79~J}KXEB!G_T?iYGY&! zl$kWWjh||R(L_6}5)AVCqpA;3B%vv;wn!P&ho;E6V91du#zI3cLJfl8Eg5>kU;Rgvpy*b$ z2MK{O1toR>dvjY-v8N*hl*=f^GBYD!VFZXiMn0`_0{vAY*5U|~If#$(#?r_k9t*~w zmZ=Tj!r?-D0WUy|n3jn;hEA#r1#F=c0W&(zcB%{HOQ4LMkG(ZrI6 z4VPort9nOjQne&aVvEQnhJ`Nzk4c%zf;(D~b~Dqk-Q4?^@}C3)lhkyGIXHWSh3=;u z?ZWLrehXe8BHGv{zBYejn*u1*(5wF2-ltQ-)t{uJ&9=KZUewpKGU_>3kv}!TE)oMJ zWT5?91UyiW=rc|8-H6rw6Byq8sAaC$qvL8jeN zwyd{*4zNW6YnJ5gJ5+2d2^Is}cw>JWvq*Mh*MR?gN8bHeTdEHd43Qtij;)80ZQ1la z9Q^|lW|v}+!4KZ3AbAC$IUYjUHu9##E2hus&Ta=xP0P@3zQt_Y?VR+V`Ur=+CG*L* zwma=#0bsWOx!s^-ZEs{?V`6RQZe;kMp2Pn|8vU~)_a6h2+VX!Hkg`yyUVg$N+aziW z$%I2KZp||le4|E&%>-YRGJfDbe|RM_BKL%m|BnG_YfDG_=flisO@MRQB(es~XlZQ` zE`ufBfyI2@b2t{50)Ji~WmNf5o3L=yUQ)i8H+)!wfL1Bh(#= zXho?F@4-sd2+i;YskwjuN};!Phg1btFSy`6!O!Zj1kV=QAtQ|i4TwIFhz?Kd$^;M; zAlLU?Dd?b41~~Jy*GQm%Gz2N%sKcd2!u|8GC)&nx|Jjk-_^;p*{=Yoz??uS}L9M;y z)~)CH;JMS;La{XEH8E+davGSNBo(@mO8CS=AfOc+kwcpsJMEk7FGieWUJ|~9-5?R+ z{xKh=I90++1QcfCWUsOxPh3swZ2SCR8{kKh+L*WN4wbSjosrx1jSA*yQhF=#%>;AC zIGZ-4x+e@OTjVsD(I)oQHEm@;y)H74DJb3uhoIjaIG)9jDSV^lr^XwN$lWI~JGp^; z7VW!@zhw($9F8uy$4{R9qsKk2z-|W$XNcYP%777?($<#bF~#dZx@UKiF%!y${qPky zg3(9`43NkaK1#N;O|!?4bLWz>=f|9kH%xomYgDvWNyro&)ZYPv36kjE?lA!NIKf;A zHp&4{_kA?GM0fS0=|A!jF;IQqKkam(`S_qc86#;OvCleM6-|vsXGx0u48QFAkexl!iC1e@VcW5M5+f6JcmLV2 z?BHUCTw9acY-7BYzWVy&=>h%+w?UswfYI+0;uis%P>Cjaejl?y?R@6%gg0jjgfl{; zgG={RvF9Ensnnwps4CKp(gd5hLOK=PT~EW!h+7mihtU8nn#x;A)4>;{<)xbR0q|kr zmi;Nfax)2AIW2By^&d?4|NB-1CBJ=f4*$KZT&V`>nQ)l;m-m`gg#@qlw{HvxFi?=V08C&=EG3Aa zFLsPAXn~$skm2uCNJgYG`C95Hc?<3)>sjTW;j2w663md)%9Tp%R?AIm>X*Cfm5ohK zR?UseRf#;)?_T#th(p;YSJ66M&+c2tU0b`HHr|)~KkiTaF;0f?4(_+_Uh~m>?)n3l zKUYF|J#NufKg#iZ`nM17dA$wyeT=SQ)xP?p;kO?4BJ3Vh8BJW%f8)hP-^J|?BX^KP zCuj3ozq%gmKgLZ*pCmH@F1%AU%XH%>N&0QyRTQ~5M2+#c ze^neQmgl(-tfWZQBKy5~r}lgN8DbvVt3eEv8OSL%8g<;YHrhNe7U0<+G*rl=PQo`H z=#Rosem^I{yH>I#T5i~>Ud23O8M)J~XdDStqnvbZxO9+jjMq3KUY@)ZZl{g*i{&O_ ztz+S;FcQSZVEm}9Xh;dfoJg`PT4XO&m#CI~g%zD3hQTSjcn9E=vaHuJjwCnU!k!eLS1snXu8LX0%@cX+Op^}!@ZrRS4t`3vD()I$! z?h^4uuefFeezh5#zcMUIpO;d)pcZOR0TG`;0jDE7J}>CWoNfmz!p0(69lVn;VW?_D zwCpQOMg`o^Uao2<=#)$S8>e8cD$^2n*swo0esFx3Zb5MwHv+V%J5@>V$REwis_;j; z?)c_nUmX(}LjVW`qy-ZPXCZi1YR*4$4+*C}DTRPRur1 zbeV-`FoQQm)FcwDc4l(`Z$O;`(+|c|6oOum@U%@N2}?{;W$)FF2fszp_Co;$Hbirb zi*1xJE9A}araZ0QuE}0i)upE*%2_O9Llm3wc=$$`J|@YdAXczmSvWd9??0NA#pNA$6;$nVdfReg1s` zL4Sc2_G66bb;<{)sfozXaBNN_gDlXlYt(#>8;hLTF{x@G0wTq1a2>fVx|!4V*aL$^S&USX)Qe zO9So|a#d-e&437Y9Lc;q5?GFhv0}W?jN6f4SL?;-t>v>D>{q$#+SU_yP34oKkp>8w zdVEKfv9O^&?p?8(>cyf{8EdgWZtwA}u}`*h;<$D<8pc~F>db7;%sLy~ktU@tG)KD? z@rnNJF6TGd3s@0=EC*FxhX*3G$iFg6XnJ0^y;@GSsy9#qzj4%+#n-S;eyJyK=uxVf zIMO#e?@fgSzWHC2BC2JnlJTr0>+)r)L5iftWcI}}XW>z+^|wRTI$pej3*>e>IhX3j zY5I3a&Z)oa=jt{yPlnVW*+g8y(%-rnt)FkxWSYwM;=$E&@sC$C_TN4o#1Ye!n{a7v zBq(-A=(@PfSsxy2CJPCts_TumTWkdy&lv)SEYx!oB)!kjrL#;`+;0g$R{)7!W>5l(@aeTY4+ zrJxR7V&;38o+|_Ic8Q_>EouBi2R0Ez0^GSNp&q93;^}74dRMKd^8<-&JB5`bPBSd= zO$H+c3Sep<^s|z%LHD6-;{5T>a>=0vE#n2&P^qpzbdD#|cnWWN%X)pgY|+r_<=dyJHgO^o z(;CuKdTsXPqTshMA{?h#OVI>937HKzk||bkB>ZEqj5Ip8v@dsCOYk5B~DNS0F z?OqotF2WzQp>f`X*&e3dp~tt-NO;4IB-R5`-81Ua0H&T$cl%t@Mpq6~{1kPh40HxTb zTCee=p5zdcdKdn*^fjXR;ikel&~`+90X*I=0Z4AA#8U#Lfqo4I2`O`x0i;~-O-sAo z>#$!ZQM;Cn%tfnn*haR+2+gZc534e5cP=x3>OlK%!Ky%Sw`7|SJ=RIkc?u_9TW_kG zEI&}UYFFKJh|V>+k9o*dJ*%h4b%amfSVg%}Z1!)FdbsBZ7gzNZXooB$r{CM%%`BTT zHosPd1q}C5<|?s({Uz<~FqNmtHRU3$1AZXuaX9CL2WYcEyoVBX#9w`}y|^@`YXR&a>@64)?x5@jTY!2W-F9A8m)vMT@Fc z_X83BXievb7kZCL6ya#iya(4#Ztp%^o0?&nH9|Bz?yZXD!I44g7qp`rg^sel*aPt5 zp3fW)=9S8i$CJtNo9Yi{>K~q%!EO_SRxR9bs2P6r27}; zn4q2^xZwIZ5f*bqlyrU9OAMWZl^toW1`x1Lp@I;|6k?K!hn>kl(@%97?Gj{KB*m-s z4X_N+Uzx9f49w^WAGlp%h#636(fLJY5nd_>JD15YIqt)eA5m%sbU6*{u8M27ruSd5 zc@X*vBjk5IJ)=_5P-$#cTY6B%RN1WSZCfV=bRz=ih@ibu>F#e_X|g!O!}BKwwXo5K z>BEnWUOb`D9B^oWexd!;Kxl2j5;TLcl3qCeG=s?`EZ!hTISE_dxah@Cu>liJ-dru)jes+FK_(a4tudxp|F``W>;Z z=fm>d;$<*mrTh#)jVfrbcXNx)6(&Ci&L4q7*Aac9Ba2C9jM(@?AJM@#NayK0ruQ^9 zpE5@u_Mun8_rDl>=f}#{ZCkV|ww+2+v2EM7ZQFLmwrx8V+qOMpSB#gn_CDv{^WNL* z-XG>4Fvj=I-rH!s_12#0pq}~`{GOi4if=mXAk>?x`SL75g!}1a89po)^if0G20s~$ z?jF(44=U7HsVn1Hjy^%*y3y&8yM;hmBNp92BU*K}!nbcXw;2yA%z7-%26Gy%pwpJY zjCRIbQ*P*%qZ7~U;}gE0tg5nQk#YZyLH^a7*eVUciFQC;S{sr8Mr<8i3lm1yezy-HeQ zUf^DviZ5ahrSs9E^;(?AD6DYK{6qneqQ8{zlE!{yx@vbb<%pP+g_gA981f{^;i55E zZjHop?c&^ahAEM_UENpfm`(xwwkCEHQm6u{k>4MGRJQx2opDt)@;J#G032P-0L5rU z$Gd29KgiHs6mllOXZCEnRjWsb~nKY=HHi?=R#!1r0nd~Q_ z7*;74B2~WmZG+e)guY(^bBD0)9hqzqp*5F^h{+D*Lq$P-`qr#uuN@Z@D`W$L@ui77yXkT$fvKwKLx>*r~K~1Z~{wEKTz=9prp{NQTaw_?Pd5E4#+D~knhb&I00QvLenm_PbWO4AjamCeguwg2Wznm@YT3pLVDF(c6tfzvJ&4kdwj2U_gd^QB7ib~S%%ohS()b+AgvP) z+>H*)lK_8Ge^Ahl5&cBT=3@;yry~s7WVo!h z&04*;4lNd^BafP<%u(XL^*nhOx+&}Gtp15rvg8>1BtA-D#S{xK0+HySN)!~c%p&2*+ePo3gWcHnEYM|!pV4H z3MV9cb#&I>l@XTFW-Ka4+8hBx_Cy@p^cHEvc}q|zidenv9VC`ht?ADJU^sW%>#e)r zHvM4Q5q;W0=TGQNW^vAWL|gSXf<@Y~In5xhiacvGWC#Qv@ns(Ozh(7>9y3)J)$Ega zK!lzkM$Q$u1>g=V-cxglZVyV{d%s0z59Az&3f(gq&oDAY`3RuTO_wHT7bM+tG0#Ic zrs)*gc%<#!Lure47s<|Eo#?+9)^9mDWEndnIN=Xx5b+D!n_jgaWV01t9F>Kgf58{FRdp9RO34QV)e=6E1)~GW%Ldk_Vhq?RdG$^RZ^4EiwyFHKh4+CZwIy+f08na$47gQ#ZP< z9Ve@CE6qaY(T8Zd=29fs%L+7)33QDODKW1i5@Z;@fxopQS`5N9nc`SH-g&dHQ-2@# zjfgg=gJZ$5M3QWhImg>)%Z$Zi3x=2=*cXxru{%owC26&A-dT#)d&k)R&cdjK&!CKW zrxkf(0d`cbFh$rT>A?Ky{&P6;9rzP!3T)j_qRa4~ie(toE_JtPOctH_+1k5-_jJ;6oW>(vB= zUdBPC`bVuqr359qDjL{&aQdl;UuzY(JxF}_i9Nf)cx8BI&IirmlWbF7M_zbTV6*qE z`kL7=zjv7dS=#Cv&84)->Y7EGDHqS{Sw>U!IF@$6cTGmF6ch1w+VRRN?y6S1T229N zc%+=KEvYPI71IoZau`V}`?{=|?zt+pl7mj#S`so9q|jiIDNG8CaJ z!8}bHPsM=`sHW)bEYYdqt4#uuam8HuP-Aa(w5B7)FdI`n$$L+1YHVO%IDRA;+Thu{>*m>}G(EGvz~t?7m++PUwQEU0^$KOF5@~C`b73 zMQygSEdddH-W5y`BR>b!jPK~4q@Cs-ww+|&5~chk}5j%EyZdsPVS6E8#-JH=NpDFBKuq;&2E3eCxK+?xe9r;NH{5BAfr5_GIr|)mp zW03V7J$y8#f$SD3^8htIRaPort`&RtA743ZReDFoUf8Zveh21+5g4JX9k#?A2ffClX@=ML5-iE}N-KJdrOp2gdg#-$sgIGtp~WuAx*2*w zW9g394CTu7FtJ}^$U?K5wam2K{rUGCh!BlY$rI(Qq)0{v@B zk)^z;i2b#3vT3C!sMZC~jyXxMqL)Efbjg$SO3Cl{3=;Y+(P_?ppQp7xkE<6>@kKaYBUN1xTL z8adJX5I5D1(52HwXd+hS7TF%hHKvWryAfqE6RiE#Seg*c4+{=3PJK?*kwA|MFh(23 z)Nnl0u&>CRzf%dz^Q5*<=pa<5?nV{Y5GL#v=8>@BR*cAtrQS?$HpK7%>J_2CEKQrm zH~WLRd?-G(ra+C7vu;_UVk6C8FZ`R>$WT^`I@ zuwGaewkVGqoerAWFVxS=7niCZ4daeif-$s8U@u68BAAY4WN@eg(tdtd_I3)+w4S3v zpMMb!T&}Q|og;_K)q~&%oi>!~rH3Jdz^~K;K}X>V{T>qc>A}tQ)EMB1L5K-XKBlH@ z2Wtt_8MTW1+(bcen2ta49gB)4C|?5W-)7H3@#gMcJVah{mh^F zsbEucX>OvW1ziIkk_R0p`E?x9X>>DI0T&U+1`6e#OXzO>F7ze(%v||0j~yZ)rn}=@ z9Pg+OZ6u-0F;k)C6=IQ0bHsgr;Ro9yCBTzY<}Lhq`gM6~ z=z-7EOT>dZ07ia@;Cv|A=n#gxQoyEM@WinATf%%rL?rz3YjO~B=0Y>j9t z#r1NA@vnH`?U^U)#RrzK*Xa1oyyi|xhHlamUN#G)CUbYXXw~B!TurH8Lq{O83THRn z(ZjK-X;gLE_a8w=;Q4o_S?p5#g+J;3u>Lhx%MW+5ivF^iv;Ie`IhOy60nWeboRG1j zp@X^I{|Zb0@_1JKa_N+zAvK3!3Tkd#5fGYT5e@+5Zxa_+_!R__-Yf?-G=I;ODHD&J zph!#Wjp%Ij0~hBtKazHWjpsPh+BAqC568so&d2xGv~+umv=AQ_hAnH=v+BHQ>7&cG z%G(2wLu(7X62*?k@|7UHDb_SA5pfu)HWhBHM1=`IjD-*9WF? zqbEMqLK!Ef)ww00oYH*|xYJt;%nXR?FCZO~9ac14oqbxp88G82<`XAV!(*0ImZVfi z*3j2+MM^B7XY>BDUY3C+9rTy%Eq>vrio=v4nFs9A+aD;;*gujfcH?srO#wj#)ZH+S z3OTt^WskBL1*5cvs~d{=m!xJkEhbmjnfOuoaz@CsfnV(+}BXYi&ef`>jjAAAHhI3l19a6 zr_;)uCDBVK!;nm7s-(_Pgj`$lwpWgW&B75+O7P?-HEkGk3;Ylc#~ekCgs>$`EfQl& zOBOR^o-qj~(qiFvjj9ubsJ+D12!s#wq`;5$4EJa%KUAOdt--JsRtQhOBbM~1El#2I zkZEw*a=Fnz#bX43FYMingoUn6838VPHFgdS}L{s_g=jWW1!kg)mt@B5PLo_`xqrYb!r!daJr`VSd7YiD7+OKjEF-x#E+9fw;)U zfNyhDIpC37XQ#R~VHlj(a-DCO0b_lv+U?!y!d12X*?5h=mf7OPy5qQcE6wpbM=8gX z;wog+9C(Ikz#YQf_^`Tqzzia0No%WE*c8L|W6mQvSyAKF{WKl|?m7}8kxRCNOB1H1 z2k}xffRU>eM77lw^3pEG64Ih|xDzDCvHSE=+fFysLs*%s6B>6N%FPoUXe%8j7pKJAoqt-|D)s!PGWVTQwd7^iLN3>NhT<9ec+VplM1)*3 zPj81i0)QLb8Vo#GuYk}!R#OWhimQ8SL3eR zxUdx0JNCZC_SxzWr!og#0p}YiSKp;lXmTCBbd~Mf>tnf?ejnJD(FtYY9h$I520^D_ z+!k?aMP|n%#14gU8;N)hQBYQ92_X_u??Rc#nNops`7FnxS(ZhjQcS!B_#lsoENUDX z!~A3j7;i0!2DFEr6(L$ zZvX=awzq3S&DxE&!#=R~U^CuXo6oTA$fMaX{9F6S$nX=g>Aq?9XCLW3C18(r?+wTN zt=II0H0MY+UNR-BWoU35Jhdl6AwaR-=k{-@c{j|2pw^dGNgMi~WD)=1%m3dvRr$ZQ zG+9d0R@h&@{5n=w)vPua8*B@Sd}Kw=3Z!tbY)sCA$bgdKX5_uMd+_w?EOb-KVVC7I z0d^Ef`<*%HJq3JxCHcb;zT83zg+WMs8au($U|?_}nqQQm$+oQK3Txj$)w3VBSno`_ z(pVd|H{0By`QRn6bmr_afGMJnuYZ>9lY!yf)DuRt8;r=qoQdfR^_Mzg%+poxwuUr@ zs5)ix4I!$F9xzEAyZsPHO`y<0HJgUZ%EZH*EGeE#m_G^I@UkFaOqiTB6>;339|jfD z&=W1!C|eg9%w`^gJ;84%?3n^pr`0gwmszk%@bI^~mnybM+7p>1qZxIpYwwWa(xJ+* z-_Mt?R@p0?XutX8*<_+xMVOU#r!+J!QiL+So~%v|%Zi3x z1NGnZ)x{$=SCR6R>p{NAJf!vHFPDqO3D~D6HP$RV!>9DhsZrw<@%N62|G8}!aVE6eLAB=Vf17gJ ztYvZ?-$+_zA+7O1j7IxnH_f$?N2PP+>#g#DMp%TtvAic$#&x&>XBB0Gofiy7`1n{X zqG#2~jas0RfcmnAEdAd9?P6`NF!?D_STjDo7MVk7@Cfx9y*?tNnf*a6pM0?d-NYAI39q~qF_wXJJUi^>6xT&03$;Jm58g0~zkzB<^MV)k zt{v!FA)mM*xzD?y1~X<1_&+J$9QChYk5y-}pqXn3=+L1(aTYRWga=%4F7!|D)yzUG zpqpAH2LM$1lZ)q0k3Oz=PK)3;dFZ^n;aflat2T?lUf@iH4hf3FV* zDPzeCxTAv->l^q9p7h$%HdONesp_~-AIr}&oV`e{Ad^wCRJt_bhz z_{p$Gw%*GegGP$cp`5d?aZ0ytgi zLs*C>QwlxU&~bYN!@klvVfwMR0IwdvrL{_OP|$s2MQflb8lHp)>;0Du^$lG4bxn84 zwd^OURvVHS`A_BZveIP4ER|Pljmc5>ok0UboVc>I{+}p)9`P$@GuJn3>WnBL0wLsy z$R;Bt!Ott0;dp;N>OAGox5zI{UG(+)&yPy}e?;9%wwA^=|DqP66tr!T_))m~muXrd z2E&yX%9}3ge^w~#Y8cKLSpEWmu`+uRLTfLIsFh4E3i})dm!^_tx&ePGh;$8+wgf@e zHMz`U2W&W+{-uEQ`9`*D476N}DGEfPdyCWvLa=fm$uAOjCHtfhpDXD$7RzvY^m9DZb-2ZO0h~ z?hHV${s3>&FwB|q^4ZDJ$a%Jg$1)DwX!91%3WqXAYqU)(9+hcmfAFZ8p8TbH!xTpH z;u}FX5k!sJ?M@BP%<59ala{`sKix1~|)O8UmcekIEgAZ1Rzx zWjkQWC*)|7WGj7+FCo^r9dg$Y6)={cv!sGA5_MZAn%cT6CBso^1#yQz7!MZ4yt=<+ zQl|mk&*(qkdrDxLq+9ZSiX%H@)gve*mVAz+l$xHV@4?t=(ecH^5f4y@M?>@vlNV|G znYTFxHSV$Z>4LZm&#>_qF~_hm#Z${c#(wAT$2oWkQgBjDJe=jGKq0_>tiwv2H3zuL zUr_et&<+~z*-1iBF!KFOEe3)|j}^@MAh4zVNqgf9J>o48YPS6pJu(ZLzX+t7M&P10 zBBnSJ$^{>x7ygvgL{;|PGgh9YlhvtNHUv(n>0FAwlteMd(v3>08v0}CZ~No^}-$Rp(&H_MmFH6+HO0Wlta>z$}XLUfKFK>VmIg?cM?s*jxnK)RU0-w zU6Xn5D<|bgA*k&=(|BhpV?zj1hL8zmPGv08P{ITQZG$3cmGFv!Ax~5&e70Lz?Bima zH;uu7XqLXn3FH|kbMl2c-b^WSBd0ERv+pSxv~Nags6TTDDkkuHDj-UG(7susauqWd zqb^yKIIq}G(VW?qeD|DM4K1DzEUI@M@r2a)yC&9A*Qj!TX)ATV4&^@|$bT*r477r_ z*4AH}gi_`<#{WK(#BrHEK9rE@p9dIv^i3TA_Z60p!Ohd9U$hv3Y? zX={%9z_0Tn=j*hB1o9$$Mo*qU=Xo8UKc3%!`~*wQ(hG}y@@NU&XMF_D8V|0Gdwr64p$0u zfq3Sjl`kMAVmhCOX*?BvR3og)I~DTJsu!9?jxt_ELX7njmd{P@GuC-YpYClESOk># zNADP!9qVY9Dm!oNd>W@1io|4I(bheS1TXU0A;f(~I*QbG)60m0+pva2#E^z>ENV-~ zyWi6m8lP&Wu!d3cY&dp6y-bjjIEoy{owBv!UalDW@(E!Nye7y4gctY+$^*Z9`owI3;z0pIItW3tV$?1W*Y`wxVjtt}#9GFzqK>J`J#fY~GKyi;MQixWU2ur*t~e6WL~w}gE;*9^2;Ml7 zF@u35^^Sog4Vs}il^Sz?Vx7Sx6|Yq;ai%b%IB01l`9ls~Wh?|!3NWU~R5N6NlzAhh zMd(PBE;F={q|A(_Jg=bwjIozV>0GdSE zt~<42imifl-HBd-h@=}0Rzz&R1^T^LG*77qMzPKcMDh5b1?r)6XCr3w(kP8EaibJA zXn~iuK*?2Ui*J~LvbUz82tInv!Lq|(WS2?!v5E?7%6)#Xq34|X7L$b2F%R$X6nE*d zNg2M2fi-0cwe0g1gl(r*PKk99*SHEZG-QeQhhg{EjzM8M*M(@A$DB%0>f|O9TSD~9 zM_b=_l%QB8I~-OmGIBR?N0{<}7Wy3lb$-wmBCQW!oHD)dUpoyqaX}A()j0*X@uARo zlx{(?DD4ZtP&~#E3_%DgcBsoJFbpvCFpQCOkqnXak&IGwQVde`Qj8NjSewB^;fvCX zP_Fft47WJix+y!wF>6Z^ZIOTQN@PTr-D?@F(nzfJ@#!yI$oJ2oiZTJNU_iy%LCW>x(W;yX)nD)N^HKulLNp ziZcW8+l=`dG}8@+wM&Szs}IvFDx;5NPfz81Ec~%(eE7oj3Jt|@4F8%kbNZpb`PZne z5Ju6dUT7S`AhqXBc$S6kr>>oMj7|7%uGXP;tFPbqGjgW%bUIu>wgB=jGDm1h;(kl0 z!}f%kod*Hcu^~;^R56>V|Y@R87b#C*fBVnuAVZS z#;yR%!S&|Q2OPKHON5uknJfWU$W7{dOMyp-P0D-BylJRS(tGwiUG$fZ8T23@!Q%*l zZt{ELJX+}YT zJV*Bm?^`iO5*vlR)?N_Z!ItFj4%@c)pus1XNnLi!R^`fQeof~%6-rDK&Fp*|HR^pE z&yz@izv!do8fLaME<}-dwIN%%cmO{GN|Q2ZLFgqZT` zKuMqWRcSV7e)`Kcw`3_c!%XWmvwQY4si4_y$h(pzlsLPG?U4=9i*rA|EEwuk{a0_mzc4Y-T&(H&N2Ld1;7h` zHs+Z_Q&X|=Eh@K{5xbB{BIGL+(KrAZiVP(gibnVEs@Z*m3%0|iQ0t-M+Mj*h-p0$Z zz52G)Izc~9PddRo>}jJ+C(X2lSRJwGLaSR;J7+FGsFz6tnJ{3z!T4JY1Fb^3d(}H4 zS^OXntv>EFIQZ82LYp#?nKj=ueo%c`Vlrqw-#0g6C_LQo`OE`wCIW`(P4*c{U*(04 z!i$j+X%b_VQDxu36B{(>izxGB_jK6tvC$M&6r}H4=ve(dvyr)&Iz7k3kA1@&y>y5^ zcMkB@r@vQEa8Y7ix4zz(*gw88qJO}u|NCUNNP8d4^Kg$PuF*Z(2FF${Fi z?*N*hD88taUz)dYEBVig)xnBMXY}nl+coe@0m6RNe6f(QwKCp=xyg2id&B9tmXvx^WCQ7hd_ienlFZ8N^Yy%z-pQ0{0y+Wd%asm|9VF+*fbRf_1zb zNLL4H!V+4>3v(qc(ypGe4#Bo7NH+bhu&~1}KUHwvI<`0v(5|GoT*FBeRjqQ9xAGsYIS}Y>-Z#M7^6c+L+tD+qf#On0*_XmIU z<0<{GjD@DK7)@GcOlrNmaVH<<`**y81Q=I-cOMOJcZ~V*gNE0%3Fznr?@TT|TSz&o;?4fZnKK;Xe8|fDMisJL z1bmDHCdi-7%nd=rl?|gNEZ8L<1l=lCeCugG_ky2-pAsd2Ep7Zs=oJ!1&%f*-r@3i8 zr#_w^ZGFF`_BJ4d*mDLUZa~H}0ftra#iowV5H9zyyE=!9p1+~BzO4ID8H81&JU{q+ zgAY*awSW-kI*4Jm>AVyji-8UC|6|0VNowV2n#Af?leJS(i>>M| zXU4J+HiY1m!LX>xN3oKuQD$2V(3!sw4hcJU*HYAknG0_#xpBKsPc~O^Wv(c_#0uD; zsu)VPIPtooAb#burIX6)&wMMZ^tN-o)MDi**oh9_pu;n##a#LT7kAazwsG{$NjZ@i zeg?J|pz+i#6-mg4Rbk>hLo?y>(3t^&LEO0sJ%vHem-GgL4zi~>2n*+f-qUs~y#s-7 zA+`%)9BB;^|7a!Or{K}YxVUVOr2O@CBR0e|$AC-NUql6roj@Nx?sH(|71|LAWMbqM zK8>*>DnCTh{I-&O1H#FWz2}L^yCWJh8big{KT{@3t#Qcf0@S~GoEAKB$+JB6;I%yP zvf!azaY0YiZs$ZGQ|`TAFfn?)wz{f!?8>#`blSZ!dbYL7!l}_rY0^}!QEsiVXAHYi z3`|7psoho1dK&T)|I13>&7E0!r0D7@(c#&OCXMzovA)fKB18XpQ}FXBS58zzn*nBw z9aJm|&uXSEZa~X`f&! ze6%8n+916exHK zl>1B@!Oi+j@ALKW?W11!>wx=MwC2!3@NVIRC{pn1<}2 z2GFeWT2DU$uFQ3}vvt4a!f~QmY1A6`vIPFPa>*bhY?{`qIX&)ac>WcEyCwi1x(08Z zcM!)-W4@pPQ!mum8Xns_s?~FH=V?QE>FLriFRG6eq44Vn=*q27Y7d2h?V~{1sIVRf zGdgPT{xXm$e^}4AT98xZHYElq!P$)eP%}Af{=~Q1xwuJy$41(JZv(_5$lu+-PVQad zD4p*`Xa7-V0ySwVa3&5olS|kgO4Cx&LsitjhqA2nJ!zc}>i{PhkQn!>E&8Gs!wweO zU_%#`NAu=I`U2bVqs#wy&Q6oUvHc;I&2_;jCK!wY6WZ~ay7^Z^GRV(Kza%p>hp?kO zK4nRzBogi7EL1WQQerh}C)lO0MN+UyaW*B7{G0wq1Zftg%`gU(~drq~zzDL6?aV7B^jSYWW~yh7-7y%&(22EhZwm7 zAQMEl+rtcRUhhI8%C_4B$%e6!?k2p^*Xxg47h)+K?CWa zTq6Zasyd`~;Ho;nRDd&jFc6p?KBXdsnJSb?aiE7;o~Dbb9i9foq%@vthbXDKu? z)@EVgU>CyVaD1N4bReoJgk2A}6j)ji+T}PWTXrB=8?l?sxp&(?h?6D%os=k5ctRv- zDZowyU=G~45R-#8@{c>EX-e05$<8FH9-`9dLo`TFmCRZe-c_ z5B+^kR;;Z1lLcFZH7}VC>et``Q%X3iwimfdQiZqwv6UJpWIL7>E&!uE>B(`l`$&M& z*SJ*+)nVso%z$N&7|F1Ji4eJ}oVsi9v?ROFl)0jsz}h7$n$q6TCH&l(s{u#7^uW-t z*dY>(!qS^%K!=gk^gb4iQNGli>CRTFq1B%c>Y|_9sCn9&;tEZ`B+JfMSxEpv11_5hYMq+vrbB|rVNJ_O2QiN~; z>#|U$+;|h}{&4X=?vD@JQl0qlI>FJy1ENDQ+eyaap_t)Jjp%+x0tGjTK?zvpB?r_i z6wX;5jt=1B`{uOQUE_q@xyzs4N$F=HVF#q}(J zT4qK2j5`akemE4EqjX%HtHC|XBANM}x3*V${r|iIJWs^56Oll{-Ifw|ItT<*$+scFcPduCJi$PCLt^uEE_CbETpI< z@sk+?I6K8D1R zcJ5^2LE2H;>~BzAXO`*3IgSBa{DEdn$H`Jx4S*2iwAz@AwFLEpuZCYZp#YTH6w;L z7ZcrDPXJPiZvyBy9=w{~YnAo$d&>B!XZYzm*SRqe9y@T#XqV{ewkRu4E#kAJkPpmU zHQQ|Ti;xd?oSg7I{%XvxV?%DtDh4i}Z1SYt=j1-eNqjDNc^bRg&8A*0 zWj$Vml^8fZL9`WIE-g$TPpHWjuGioXv?d zgtx$jME2SJBB7i(hN>j;evg@nH#l;_j8Q6s#)W0{wD(_2U48YmGou+CjJ+BY!Nf-U zB>a1St5l}=`gf$i%&GGKF|v#4|Ds?08)W>?cJ*JR^_$v?m6-q6Mwp5`J<*tooMzFI zfSF`s0BBJB+?uf??7F(kiHYH#S^FW;VcwpbMa*IFEW}cVf|#i%QFb5K1`>ax^|5|# z)2qq0ZP%%`?at@dobAtVba0aWsVTf$u_pZK z!sa~jN5xE%SRQqi2Eu~8x8h!r%BYqUHsoR#PkV~HulbF7HqfBJcaM|PQvLLs^>sg< znl-Z(VyVKy57#;UFWR7#=IbWIOUcBR11kJ4Fvbb=jACeUm8|p0<-#-5ecIZoH~4x- zIshrKj>C=3LHh3N>)D$0RKwYH#RV8`n``x1@w6ijG%O$OSIVv!nYF}{{QQr^>rH{< zP#g?~6;K>^I^rV-R;pA=Ev&)a1~we}*4f4!6~(sW?u{#$R;yHpUR0YVN#u~*BaX99 zl8jB{&%dd4Q@`War`4@&>Z~+=vW#o%EDTM|U!B|v3)hAs$KZ!cHvsZ*tQO&1S>`?3 zhxyK(1REPcvaMA_awu_eHD-)Q6ts~N()Q?&;>WUK#kytPU`cB%T zRresz1b9eH0}C}B(E6fLqrWc+)3s@8K9qM6SFb7@2Zr_!6(Kow1f@9hFos0qXC75q z9vAGe;Tdpr-RIn2mbwXdEz zYme5?@mF}#)GjCvt$~n%cYbd7P2WUX5_J~?#pMZIU76n7C$mUzn22*k#$)iH47NQ+ zhlBoIWYMvq3>zm>VK-Gkfn`EWTPi&t7fF;Dt5A_avO8CS^_5A+@RQPSJ|0iqM)erY zvhI~Z(uils$BLhAilK7P2pL6laZ;ABIarjpcCxp;uyS#+`TME@6!`Zjg2!c{Gm1!g zB@_a(`ZJFLaeYPJ!3~o4P@Tz}q4Ar71{W!GE{Vj)6ov54*G}_5tB>UgsdnlDb`-b@wEgZEcq8O4skg&gF%8txjUVjfhY; zKT+C&T@~@DRkZpWLUP8$dq^+HQu-6vidl~_KJJ{wy7yni9+K5ESjENVGo-V?b zM|!<0g%%_$3MF%BwQ|sN(SUap;0Ih<;B7oD60r=1d1^gR5Kk0fm*BnFH0e-JlMm3p zK6wuF(bg!xNNW49RFVHYQ|0)-C`LlYCi>1+PXFQ&|3^tGQvH`-YqNBNd3#ag%u+Mo zchI>;BDLB8;o8F{IkGtkYdQT4s|4!x&Dzy(6i{SjUENWUw7PFQ$B~TD42GIT(OwEB zZ^uubKc?F zW(f8~Kr^j4(0wsR-^wHy=G(9Ur$1^KzKk0CVS1qQ(Zn&$aXF@wCY3`dLo29M(OcUw z1}wDVZyXRT?L~$t`5Wu6R652M4=FlAmW{RI?3D$SNsL#gAT_b)X~j#=@Kk9_imVy%tmjkJmx?|2|THrvZMJ&6)S*JQ-6FB*`r z8kVFl+*}Nl8baMNexOlSW0fdr0stGi)8Z$M!PW7XA`L>~4F#`F^vsrQzy_9`mp|(u zg3-V{cf}&ZK;gB*Ee+V&?}x3WoqzQ-%V~;lC)Z|QrF*?pX?YRCx7YVAF<@m4O}(;d z8}%=uJCibxI5;{x6pDRIhSfmS_hYDXR`i8%eh}@ zqeRhUX`<+xB81%W&ejMOj2QWow{i3i(Za2xFevf+PyJ~g7eM=Y)iLLn{&qb+B}6=Z zK%mM}v?aLnR8O(N5aJR_(WA=PU6m#(R~Wa}MWVfQFSc6(O=8FC3yh#-`4E*l?WB>G zsvWScxS4$~VY?+JB11&spmphv^@h>4D-jd=O5F-q!{kc-&jDrZKB$L%zW^7jyx$hz zllVg&*7y1QCU(@=CSxH;;Ar} z@lX8;>AOFljGh&)+2QV}*#Y(<+b0CY+ee1vOm?jPN`T>piHx?iH{FUoOGGg;Gno?) zvrM5{7gZl0D)GWhR`ou}DZ)WNUusL054T0mM^Bv~pWuX&5uu|eyp{WB z5avYZ)84$suL$ESjSd>uFv9eeU|A0D?oY)@qlp5R3za#bTI=cJT&=;hjYEk^VDav0 zPP`&ah<`)N->GG=)}P0LWfolp^^~otRCRcjSVMx&Vw|m0NOBI{nrK!rYtn56Uvq?;ED3$ofz! z4C4?vuLZcEl3mMB=-vl%C9hy*#_O0js5Tn9;4yQME0-Ci;kUB#4W(9ZB#Bl-q{ZE) z1U1BLfl0e;iPQ}s?t?;f<`vN#4{-Bygh`xUogYP(C3xO!A^Z1dXb%B0(G-ttsqx45 zY~w;Pz#1}V_yId5w=mCkEl$vjeWylG=#?GAUY4NS9KEiKo0yfjdE~(o%t@$LB1PE} zz)2z$F>>blb5cddSe7q?#cRF~E{lFp<)Qfd?o4}37yB?We5jmecAWqhyF@Dw^X>TI z=42@C<~G9)iPxP?=inmoJ$_|xH!w@l;yRo7C%4TZyJw=C{AZHt^yfO_Ow-0RIBYRQ z+AIQ|XphW(4-BGF$nYd_qy{k<0AM6Z#R zLxwg&6;HLQKC}eOGdI|{)cthex002HVXc-FUZ&lMXi*Jl?$u{xBt6FA3upJHJ5cr= zB6Hm`R5o%OHc-)L+PK+pA$DrVx&DRgq)vR>7Pwax_Y)6U?puqJ``T{BIr6XUb?w5H zT|ZTHr}gJg>P`wBomVSwR_;Bm9_AP=`HmJ!bN2ft$8ASvT^$PvJZl++vG4Tp;O@9v@(ytA|@4noL6XTz+Md6Ze3L^T4nK0mA*i>^oi)dM=Z`aS9_I zNMRJek=IWqgX(=EtaKCih=`3%P?0QUe*7)^6D~C-Mfd{2r*Qw&cm9cL`!CYw?;+{` z=r>7;mWuOVqMq%dMsoAkabdp0{CF^{)?eX2AIFs_*?{|Y!08(?A?8`6(!+gjX-^`P zVm z&NBeHk?4zrM27X0G&D3on9!5z>+}H$S}%>H!cd0^?sw*uM4fka?Q*EN_=e-hGdPM5 zb2w0Qj{jUWh&433WdUeYR*_a_N@^BGCN(iU!vIIzrQP&q1Q(lcX6Zwe&QdFAHYye_ zrPkITY49|da%2qYXdJJL6LuQ2q2;_rG+p`&JJbbToSzNF#c|R7Da;y`L-1g*jymyl zr_5r&@m{RE6NTOi3Vk-EtJ;&HwVlS#W|wdz9iB*|=^xPr zp#WKSvuXpEECTTtPMc{RF_^cdNepBvjWT5?xYvWlv zLKYqQ!+bO5FiAsxXkEd(3xD18@dH}A&T7S-)Vx})qcmNTpWf#hio;&9_uC~y>8B7uBbj?f|H4V!N;Yzp^@Jysctpr$#F2Z!P;>v2K17~#8pKo z(;m868-!iP8t-x2Q==8m?I&XeDD7dtOS?2;^LhWN2Nj%;xDR~ucpL)pNb-*}hPVf; z!CyaPU~@wMv+YI^25JiR3;oynM?J#7b9X8j{;Ngqzx;GX>Tk*_Pw1cChASZl`C--J zFcGorwUiYW7HC1Lf5Jo=6Mh!P0yL}7*~aNFH&t1oPx^-nC6(vXLuIvP($Nx|shaC^ zMKUx{*BY~2iUnMXS+YhEvt-O)h3_y~KQcQ~O+`1ec((3*vaYzc?w+%qFGD}OoZx?w z{XG^OfrXe4BGEIE{tUz9V(eNDuElg8mrBL-V7zNZK}JHKq1(3LHwuHTy^Zf-LX3f} zBS_1Po0SBKg{c|_KXWA7pqZyIxpxKGLb{d(*-C)TM7g$Q$R4o7<|Es-1ld9cogNu# z0PUtx$-dk6(n}h*9d{gxJi75=@bzyX=-e)HDE7yM=-lre*HZ~+JmP)M6S6b+>VtE~ zVhR}pT2!*+x}WvNAw!9EwCBeIeqgazfFek3cWbcDc9F1^?W|b5+B`IxyW@mDh%8&3 zHcwcP1_85+>kPGU){L{l=}C;D@t;I&Jsm?{;KlEaJ%1Rc?fh&obe!&5i29{gCFG{* z5gAxPziJ4Tjm?CEs(T@J4${Rpk(>{-P(W-sW>P+!${&YGD{S{&<+)g*8xJ#;xOZAP zzvtwboYYd!2X$vOJH{x|R#oGt3P2;A)t734DVpD-RyI@wLY$VNZbU@zI~j?s*P@jd zZ*!F~H~opu$joRRKV3Z)NzmCqj;)uGa#ev~Gnzu(NYY-ov6$dYXEYXrEU|S#kSdGw zTW=IXaEH*>luD{4IDuo;W@4GMJd72q*nqqOZu% z0YJure%BI<4SNkT6%(kS%d$9WoE`d#|nUNZ}E)%%62{kp}AJF zBWsg5tBunQ3}t)A`#p0l-C4Dx4(ew7(~ZIEnay{vmkd5g!V0Ue7C7EY3`K&o*{ce_ zw6FAgumcBYR^)ejleq-|af$Ca3y2HY3PNOEAGpFkGCM=V`Rjj%!$0sqWfSlC+^f-Q zdd{aZMUO;@7Q@$6{751eC*QoC0+^;C;E>00t39HbWXt+iSo_P-yst;a<&C;bB#Hn< zo7Iyth%B};M-UD-m6VT&Z_{Fae-g{S6U9<&Z{_c(a|o)I`K0{;ntG!m87X8xP|lEJ z>SyMfvt58hoS}@56A?L_t@DoM@R85Nk#0EsP*nYE8|ti=lE|VAk+;}ktNC~C^up%Z z;H^iOe5(u{c}rjCLsd?G%-OrrkKkDsjayCK7m?AiW)o*hoCcf+`tGbQ)Pt^1=~u85 zosPCj+o{50Pq#ASgT+MNgerHJDCzwvniTP41l)Kz8d6F2!r^A=`&{Wd&Jpu6r7Kc7 zO`F6qgMW-IGA;s(TPYDCK zIfE;aO&Xk|e}ravL1jm-PxiS9F?bo^rx<{_ITRMjY#9yIfA4XL=T&k^QfT$tJ?+nOBeX24(57B-M_oiHvbr}3h^kEh0rLX^yh z!qjjc50yT-u+ci*HUfWX(jc(^JNSM4mplb`ERS-c(4b5A&mG;An2+T?QM&Mus*_0{ zYX;-^KCX2lZEJDI#OtO$?7_9jwltj3&jdcIzvbGGu(&Ynpk;| z@^OFt3MFv~6SsJ0yc7y1z$|KuRU(cgaLX(|7Nn8;5tcXrfQ!9bGn{vrH?1$bezSEy zk?oLXKv4J?BjEFgw55BP!z(7PpNb4>5PM}xUx z#n{D0+taMfi%N9>y58Ki3Ux1O^bn*DU&sK6u&+|74=yMoroHG_0aa z;r#r`?|A-@ufV-7Q^=E&j`%k)@{X{#E|-x3~p* zbY>0>CPWCNtx?hvrZm?d>OhSWlS)Y&IgPwzbeolm1@&jAKV=QQzGiou&MS#XO%-5#m1f(10{rcWRny{-SA%tk(#JnFAgF)=r?Il>I02oone7tX_TvulfjsS z+)=~ZB#erzjvGBr$t@JR$y`TeR7b@D&m*dD?O9G2-t25R06G4S>7Uj^H%FF(%wzIi zQ!?9Vw*$X+ns0fNxs2s$z=|)pn`^wmY{?ppwp+^-(UGWk%dAC1%fClK`>yIWb5`93 z1Wb}ZFl5x5pS#Ev&4rwt2yMz2>6)vbDwL8e&2tS;0wOB;Tpwb+LU!SjalcvsiUa<_ zQ%D@elu~sZCzKbAjrNBGR-E%LXg~6JPynWX8ryG)g2@VpM4@12c5*9cW~*#Mga-td zemOcKn~z4^TUXHb@whX~t^b(B18Er8%4NB9UWv~hHwTgRJ4wEa#%{{c;5(r-74n%v zl3t!&RE%rRtP&LgG@w|TKay)$y~fnxR;Dm#{y0}fZ^3!07QrnzxSh0CdAJ%A*ocHl zH7zAAC6e(RjTbdnxEr0YGR>*{VBO7gYKnR6?$ptbt?$dD3LS&+tL8T?+trV)5tuTgaJWl1h>ho8q2O;Hh(^wAg zB6dq_HQXWRHyQ#@K2W4K?*o>8VlAd#Nmz3S@>;^lT2N&~I1crEZ=+C5{eyfYi7vZk*-kPenYF@CB zV)Om3zMy1cY-gBQtLqmaM1}J9`b_h*o$Tq|m33QlWE2vf%8{3PAGM=}n8;6;BR-kA zk_N|q^IHV*vquCu73YAO-gK-@P)crEyD!bP)LbnuTQkhljP5k;;Om3*2n)_5BdO!1 z2{Kv-WE{njWRo#;C{*h~R(9hjR~Xd7*6tWJ)vm2x;P(}2cn}XDdEd9R0lf5t&mSq? zQHRAjGdJLLd%ydm#0iX7@bcx~JQja%(_&yJ%wpE;2-f0_lcSKP1=>v@ z1#o9r@Hk6it|_^~1>X(lLcmDb3n@arufyE2{$ey66+@P1ol3ZaUW}g3^GY#uXiy%o z;?g9ks1^zFz*UCS5oEC`c0;j+8`Emv02`D_H(D^kRw|)h2s%*tJw20h&MCoKx0uve zSsBQn8Y%vqJ4V%jXPKC`QkaE99i$7>+fJ77NlgChM&H$eC&ACa2ka~SVs`@8Aw$C5 z;$0WVuO_0bW8%-`dFrp2dKirKF1f(wne!{lM?}{N{wZ}b0}2f}Hlvv~sB`{a-%2pI z1bO8ixjF)C|0+H3N0wgSGBCIFgDJIW7EbN)Nw&xy0+jO>ZiAy=-XL$^j;u8oo1o4a z0{N=I$RFtJ^k&@o-~R>9&?ju+8+=)5qW`fz=fAHPU}iA>PotI1m(l9~HeE^DI{dGC z0X1!PTve=%&~K=~Ji4Vtt+hF83CbX}LLqHhj&NWkgkpIU!VW8(9J3*FUZdV=(P>4E zk9$@3eco#vhORVWM~dG7#b@9H4~sC=C|q83rR(8-=GB_HUuuK--`A%Wfp7QQ;@IL$ zWP9iXgV%Cko1#J!#e<9_K+UwpL!29eYUy98b^cHN8fepqCL_-Z=d|HFz}mkOaDMnC+=-7@Vg(n+v^fXh z$(u(T0)eV@92rfnUqVa*$x2JybSsOrByM|cPja?(*C4@sOX@+o-rPR_A4=@7C-*;{)Q09Ny)61yb~h}RiE9L{7Jz$hxXSb|#$ zOgS;}DjQfkI)@TLKa~txi`=@ar7TA}U=^Drt-MB#u45T-M=I5DxnvLBJIKvbTIJz%eLI+MLMd(N_qQ)oW8v><}W z?g$%Avix1thH-`&VwP{quq_OPhYoV6J!NZo3eh^L#T zV0$!gn#%5eFo>-^NMPir@R9`f*jf?_0l3QUdGmEM!E2d{x@*X?xT&PdntKGdnUYhY zQRrnHFB4x&tqTouj=llb#u564Hk-4^`gU^~GOQ_1*PC8yJN|N)kbQclZ!8Z3)hQ=i zSn8m&97Fk1*C-lC{qDxBOd6ej2Nv{{`8On{L}<(OBPXp0$aMzKNGVS{;w*Ia1SsWh z;eFD|y>1lt2WG9-7GhQ8w=NeEdUirKA?bkvC#ywc^VjDh(_^( z+q6J>4H~Z?MZd6|QyDHRfZO&!tRlmy-g_#L5F`!9p)2SkEUJU5jVkze5n$OtEE_C1 zygAH5tBd}IW)fb`4Du&F=m=W+`iB9;^&KU8?o|CDD=dKGW_*W=qKoUep{Xj9}XwUMIFP* z$}Zkyl0<4K{CSoX*x64i)j>xZTkuDb_^C@&u1hb!&qP43OHKGr67uch^NXy7gaRjg zfE4_CrxX7XL>KsnMf?ALr~fB``(=Opf5fXrYHQA@rf8qGiLAEm$$Ggsxe5_K=d^2! zl(Z`6Fr>)n=;%Zvbecz-Hxf*Y+o>=abkLG8877{;okk%V4Pz6`%=V5&y#~kZ1x?2w zzQ%_Awr^M|l(eGh_I`Ohf6BUh%ev#CcpKl^@jM&^hCCO#V4HLmZIHZtfyUxmhvDTA&@+ ze?*wNeZJ6N9?Z7er@+izMn07EWqVc?qtKlk#o1S#`r$ zfsVa|Jc>;4qdP!V+|{zK)>AK#^+(>&(e&7ve;zrSusoy0Nuw2B3~o9>V(oY;H>#r1 ze1IZJFRRN-Dtu}?*;qkvR3S>dLd&hDWMXYIBMJwYoO}he;xI233NO2YT7wldzBIWV z-^Z$D=`1Izg+8`)+hN5R8Hc#4<7}P?v5VPIn%wW%VuxPGL=ic3UU-}% zljYg6xi{FsiR}G(N0oLZ*CN78sip5I45ct%h%+4P0M1i#y!*r4d609?AOW>AS8q|4C9sy6GTV2 zLan--9Et<6aiWziRrJwgn@(m(gTg~zagmYEVlcPbmrq?xrVtO%ES|iG9TGJLJR%~* z;tSy8Vc{pvbZhBz!FP6Tn@qFRrCPn%eq+4I_h?OQGY@s#7%Q>kYz3pnxfHen_~-9E z?L2>Qfh%{i*eV&7SU{C!XnOEIX~wqwnSi@8JHSEnP;@sc-V z_*y2J)$|^w3x;zdX_RR;zK1l+HAld~N|(PJpcC75zijSVDV;TWyBz2r&VJAl_BOd} z`Lby{1UeRSWxt~*5+3Z;d}nlL${6X?CY#36@8lF&qWf-(Dkp;&B%(=uGl*fwi{dM? zqQH_!gqb&g;>s^lx8z~JGfSvM{>Sq>7*H`9S?vs}KMLgns~?Z0|H>##%QpcU0il~t z5Z6}jb}PV**V-&^(riP_44<3>ffe?!24e!cqDSz|eFn()$EMfLUatSrB|(sDeCHsZ zf|S+>9(GlP9{t_cMv9Mt_q8p4WxRGJMyZ-2?3Ps3DdI-H0l_>>LlBYthFFL*f+|B3 z)*jkS?l5nBpQN1A@P^8ezgn}EXEA`j+Xt0P_W`|c!mTnmMJb-T)7~+aS6+oC5i3e9iVrD_Y;TK zdx_4Q?xmHT9$i8f*_QrjBShSf>+;Zmxip%Tjd5DyXqgbxR;DSX8DB-XE4pk@O&Ipg zI5oTs@r_PK;5te{$Itg6qoMo?o$sC0^z62sAiwWSYd{&rPPvC3=DDRfXZtVArk89Q z_#zARw!Q@$z2mXYp+&0@lb&f?H51tq^fnN>Gh>Itut3#~aDHW|uU#T<665D@*}Sd% zA8wGeAoqK@qK7e}#e6vvi#p6_^Cn7YzV>4mz3$K-d_rD1x%}H`?UFwU%>=9Ki6RN` z7>V9&M=sd+-Ef_FRCx@9_y-Jcw}|XDpl|7fx@9d0k^c+_ze5N3wA1d#_yzD<#6+X* zBcR^Sd|EU_(=@J_4>*U~K62I3Gah2b&YFeF=JfyP@M^w>wNv$`V|NxJYb(ySOgE9giTmK)m`~eWna^^IM#i4Rnr2} zxCJjP`Du^Sd4C_pV@K7uF^^Zgn|Pt|cER;m-D=WNE*R6}g` zn-QZ3UaF;z7zydn^VDO0pRsiJ2{5LU0v8C3I|c|9A; ze`%eD;yV{do zr_LrvBm}*_ys~W^$%Cs|!zgCpWiwR(ddor^h=@^rij~5s`NwyEJS=`g5!>>PvTB3b zC}c?nnjM`0_JUbcvvAeqCC6%6HPY8vFJ839lp-F3v}Phh>fK3I>6ZD2P7HOm6`&(0@lm;FKAk8&vAGO zjb-_NyI(|+sO;7AVZaOPzcDMrz#kyPH2qMEgjH*RP@LBwt|z0xx;P)Hq zR9V@AXY+qFI<(_t4{*bu)d>odq_YOkR2sajpUGsg8in9do00tWRLz`oz%fU&(U-VM z8Y`YMAt%zXFXyb-q05}VW}mWd^>@%Ax0sPZ;wAlHaH}8xg!5vKR|rZ3?{a{Z+!z3T=kZn- z!%)Hyl234nePpc(g|wKyhX_SJ!y%W9TzFWE0U5~0axO9q-(OE&bedBqlQRv`Y~C@f z*QWbBsKK^jB&jaMN8@Ju-Yc=ss(BLD1vUpZNOm#)uQQ}cGpss6i4I$V6ou=7>cR5C zuZdttd6gllPGIr&U{xyBAAHd;o*I8HHWin= z-*|Xuby~_cP_u!4jK^sr1~BQcV5#e4%Nu^hidR$-b?rAHJ|xX z*Gx45jC)z5L3_GPKnaq|j&x;GxIGMwsgkC_eJd>8Uc)T5k+;9CxBKN!^lWi>?F;Uh zjM?6)4#JKZ*%jfH@!=yutYcx6k6K6Yl2#{-;bq~_8?OB4l$sgGsBHw9_rc*3k!Mc* zdNvW?^!O)-&hOLPntFM?XIp_)*R$m?7wZp_jj$X<1-8pmxMe*#fEaI`KYxMmdbtU0zcX11|tq@6nO z%RDgO0EdTjT_XhnpcMx^0c>NO$Y3)4(f4t&>z=gvy!{K#8WDF<%Mj!zrdB%pRP^x+ z==EKPB19r~(sdE$8T9JQ(tj@041GE|adnNqdG)rr#qs-P{5Ss# zm_OgM4;=I{6Dbq+0VPdrCT|p0F(ZCI+=SQXsEc`AkPXsmF{FM$)I15q8 zFsDcO#$ygq-iwr!#}Zj{@@HM5OO2kU1v5NKtD7}Hc9)wg4AjTmioG5@_;`%(%`V!w za4sjc4mY}n4|V6Kl$o^{4_(FGZN&vIKv?YCA<=n}ey8oif4E1Z_WEbBps^NDbjH(9 z$>L1?;G1su|Ih^*^D|< zaP(W6v@TI{@?3QwEn7NyBjAN$Y6$(z_)|C$DE)bc5%%F?VCx*hx0KCxyKc>}c9$15 z8uOC0qfkj?E^{JEGoZScc3)>U^~=sptaldP=pmoaW-TS}NPVYTQ#m)eLwQC?u{66| z9M5ahVci+#QMFXG=u|}lu;Fb{lxH%Z#F?>s@G^mwP_Hra4Tg1?hE%6Xy@jOVD_K%& z+yto8&(o4*4MP|G5|wL_g^Pe&is=u z!GXb3r89qr>8m%34Zdct6UcF0907~7*(VB-h0BeIvpM7sBqZuzO=5k;7Z;$QQkZ!x zGnuDs(6A>=Zk`~S$HYsYIl~pQ>D@@!YE?*UnO}5PWOyJNLYUi()KRhIXxmmT zxC{pEH>DXX=d&uz&R$02dlgp&=!Y5B186M(GUQoecSD*>+Pe|i|3G48-xxBRW3!8O?LpirTd zBuWo1da^AKsv1zn)elC1uSk>yOxWm!JwZ6np_uKwp5-m5(v&O5@PSxC&ES+vL%;^C zPNfIrTXmkH5p>4um+qBwmLzg~xih%9JH0twlvV$yw6=&C9gRg*U%2$=Uvq7fJacSq zsa6yjaK=%A2s~>81K8rUKx~&jQf;#|{mA6Hv>(I$zHz18qiqFP#C>nS#zHkkC3gT= z(||ZU>>^5a(KE!lp&-4Fi~V-2jFv}Qqcw=qFI$|Dvo_~S_lz!R_lEQ| zb~=BgwnI#A4tcHlOo;h-SQ4BdnXrOn6eAu;6A3t5gv_^7$|Iq|Uy$V)fJMxkw)!As z*o$GtLv%$k61>q~&MEL`HF+hii0%uVRSKznoxYfmKj<#BfCyRMs*1+-{WQM-8ism$ zhLBE6Lk%JE_$>@h_5C*j&Q~7OLYx8+4ka>knpV|bhbI^}>Q;Yn$PTo3+sLRtKLwEaaY0!9gL5_!H?O9}!an4Ts#H*-SZ=^58r07~6jPkG5h zuYpbnEmBUK(>SI+?!NVbc-rR9&6mgsbL;f>cyX2O+^IoDYbUb%CzVFE7w1jk?aBs(^z?ACOh3Skr^mCDm;IuX*Rb(d9Y& z;GS0B;qJPk@%)5|4>=q65fjVg5xb!2b9lTk`EFFeDquDCyT#s^_uWQ+algH?R5yB| zO9aEBaj|$qd(?{U{)K{iaxksM1>n!^d>0J+X1?0Vh1OD&Y(I7ikCAMEo>Yl}kY)j8 zThPEGjfNIE=0UwO`8L}MQc-mn72?`6z>=3o2AP?z@*B>;t|@4Bdw&i|=%g?Isk9CI zOEIr!BQ7DaD+YC7F`F+Zz88sWXvWjrp{F!n!Ki8MkWihtI893UPEriUdQ|~7tm}GE za17>kenhHE<`6bMgK<#|xz!Md} zac|62O3bulcmU1ms>Qx1HXc6DOrF82pL%W#L#^&`>a*1FQtL#q`x>1@Z@%`5cD=E( z{-Rk$Y~*)Vxy7^!WvWi8f*cT_s-e2*< zq2yxZ!l%1V9oB32r4IS0PQlHxTdpvR&SPzQ#nWlxx0M|`b5youwDVK=x@Q_`XG^3! z&y|OHrjqg21220kje!m0zpulb^__~1IjWiSql6;p6UX9X<3|xp8fb6jax&`L54P&J zw<@3MTmZ8$k#vRWAUehki#SqG7zLEl#x_*R_jY35D!;KeUKtbB(>U!(k!6NJNdYXD z``QrcNT~S0yI&R@x1(nLC1M$Bw7adp>@(Vwbu6t=qGglS^So)?8RFg0@lu5cVb;f2 zq5|6pc|hh_Wpsg^nvT(a5A&t!22({Ko&I2&8(a=P z6#Szf{EAPGFhCx{;10)%?kbWMGVe~O_jnCYlbX59Si1{FDa zgkMoXr+@dtr2B>AhZCmT809%kfdkQq` zwaN#i>NIZFC&!3KimO-5i$H9fiAG<>gu~yXd^sz$G%HxP=3S!m;(V;GFG=)ee5= ztTT{F#VQ%>MDeUuvJy6eW29-M^LB2F8#jFP1#kN6Ax;^!^29 z-A-iYFEU7BX%ii)f&rb{pU;1Zvamy)G#7RDLK?s3VtVbgxP&qyxv415N?L@DS9 zdM3Vv-ql;DNM{&*t{0lEfeLst<^AyrX&OZZfYOKGw$A@-4;=go<+t({X5B)w`c1YM zcT7m%W5`Rq7v|4PpAJ1ML#@M4Z7h|i89T;Vye)K&33-W36AN+Os^KBTDj#v!%IP4q z&QFzYKmHsObu|W%3Pg#_VB)jG_7oEGShKSgsUU2QlK5b^?-C5O8KUivQYsvvm@^}# zwJTS&mAGPUStUk^@IldT#DcgLZyc$eA(YTD*XRl|(pWBevhGK&7~i0FJHpQnMq%3% z)*6sF_Tj~jJo^|br8}A5%7gv(au!``Xd72c{l{zw^0euf82Urk;S3Vf(vw(J@&iIU zQ09I?7v+{c&8L0FTIAtzCy#({v+O~nRcX=e(JG#v&MvI2S5~^*Z~+3PzzqHZ+*c{A45y1HW`@wL$yJRfo%ZfJ>jF3_n=qh;tWJobv1x*a(v1Hi}uZ^Vvk5>B%8B5|S-Rs$i?M zDdrpr%Co^XZ!g{%mh~-$ksJZp;@M5)npIflT^d-^oyI?j$1Z`Aqzl1zwsO;eLtH=+LRnZ?~0mh6ux64`#xJ?V>D2HRwWZ5j5AVx+zsArOW;;EcHrW zxOA`6S zDIwFbLF)!D(C1yRv8|O}`KKz#I8DdA>v$c-EfwE@^Un-r4^ay>T z^4+EUm*|;}=XQLVuz{oS$Co7T;xDS51JU?T%2br`%hHukt`oTzDY0>yP)cdD$uDZUZ5&5}6XnoW8FW)sE9$~ag&&kuU-lu39^rl)j8X_owO8kgd; z*E9$Ljd~qXUo(S;Nwq|NpXV)yj_zanr04{3Jo4t@Ml&ed7iG6K?CnF z|J(95X}7iiIBDnCw${>mTM6aHz|_@Tx6!vExql03!Il_j6+>h`OzJcoKfFVWa5qfk zjI~{BfG1^zB?G_krA1hNps=MbGJ z{HOcE+&bgV(vGf1Xa#3S%AtLcOEMVBS}T}WBR)Q>U=+8d9>Q>?bhTnjhZx%uUVsdi zDcA9#!j3~Tc(b$9?4(_FFjdl>3;^bi6t9PuXmHe9-s*gkLuq9Eba z;R*MkhrW1}%L*!`KgUW6Yzp@47j!edtx)oT0HHg47>6<-+h~ZXMni(pV| zy~o^{BDkBXM&^cmoWVSdxBMf$WJ~?6589Uk~q~F zfu6}f9kHZW!+7g~FRjVQ9vO&=Bf@C6NFj{DrO~B#`nQUFmmjkrWS=QXcn&FS_SSLe zPs?_J9<^=6t^oXRn1)2&kh=V4j17?uugK1I9#!>jVZ07Qx-<^OtJPWXL^@)2go;Zd zSHmh@U}$_saFj<*rGDY5ZG_ye)-RxH2xnTK3qE1NfS0i zGkyU>U|J_M%q7f2My(`Hgj=S%T3-=Ly@StKN#Tt0tziugt{svZ zP~P*(58wF%E8;?vlO-c%MTB?!kt-(0eATZPvnZJ$I(ZYUVnBS|(*u>CyT3`)@Uuyj zMfVM5EtShRWU1S8$lAVnt0_3v)YZchBlUO6!U17R(c=f=e=L9y0(vs>(BHn5V*C>+ z{@>TWFf-UYxLDfT{qHEd|FiE^X~24GFX{e`A$ya$b;aXRR643_w@l|uAe3D#w$zI@ z<|$JXP!dRJi!EAOj4lvMQIV8ON7a|ZbMJ&qT5T;SODLE&tOW$$bFm}#rz#t zdS%GqpiaCQ#2A-t>vNq(Gv$EX?Cs^qd7AI&(^tRid6hx{B3H8s;#!Osw@33mfDqZc zFBI~Ci)uWHcD_C{(Nru&e#}WP<;dhkSd-s$I}mXiHeLWG7spR0T%fna=UJIh?|P5r zxk2ikORsXk{06iBy(l0RROGGV+v2vf)6-IYC#}1m@%Ek5dke^)Jy@q(_xd+4&2GvX zedcRKes3=S$yUlnePpRm%5nAJ5p|#E^!1O#u=Nuzxba- z9rk0lbkjWM=H_6}?A;{eWSZS){ZDY-0$I|> ze%E`+{+h;t0V)Bhm9Je8U8qahL&}>?h!dMoR{GVvCmv_M+!)qZoHN`rD}JnV_$RTA zZUh%+oPSo=R=4IWYU$mVIBI{_?8OAcT>kAl~V8!e6Se=8@w% zXBDm;9-q=}!<~Y)a-d8AVc&<-gic-HX2yvxZE8FUE{Y#bW>jEYfW|^>zesgt%N5(e zM6+pLQrVa+Ta2z|G6ZCP{4jPS)^^a+bx4mGt|xUV3H&TXj}^raJDf7{EF6^5>>s{J z+1Wp)l$~Y9i?y|O44{-4-@$vQza=0aST5bLj@3dVVG?>1y)a(XSZC4){)mY&UeV|* zVo4cA8P92E8Lhjo)v?!2GDRpYTH07Q8&+<1s9L6@zETBf_X~m1^M;G8uBH?(8>t*6 zh1qb)a5Rw>xG1}t(;B+RMkB18_*zriMd~N^tG(AI$BcG(9ZwR(D()1Eib`6Ew=@=u zTg-~EGp9!=uaqYmjkpb$4lqQDTK+-i@uIXPY?!y?S*;2(HSJFcF+$}_{=)atGI@;P zBX0!~VoYu0o1#n9h^ej6X$VGFEr&%S6;Ech&Qacy%o8bW#OwwoQ(sm=tzv>dK#{#7 z-72;shMkSn5T-}3JsmZb%!m$f;tQ*_WFTNtT%0TZI-SnMaV6M})`2@y%s5&tFQ)Dz zV>P3M;tA6 z&+P=@G}kIr@9OH8ag#7Vl^BzdxUG`SHs?x7d~2CzKx?{7a+VH}Z0&c3IQ8tKtrK}! zcLvTblHo|eISwmDdqQ~Zgl849dR2=gN(lYlw-Vw%EY&DVmRbov{yBt0)Z_yzz4N!u zm=>ucg1C4)*{MTr-bfYBGT3{FmQ>Ua&m5UNjTVtb={2H%R$5jGE@sZ=7=hxTlCuR` zm&JCGnCfRtW*<;A z%P|91xbxe=$yk#vx!?fb*uw!jHV#cw&I6HEk^B}@%0xtNh7@aALJ>F8_&sx)(JC?N zH1T%i;hAr)JV!4W(+L?{h-|*5MOtn0lEOi8xdJfUP(&6kcY453GiF4ZiV7LklEq(ERXvMzt6nlqa#fffTL$G7s$FH($lr0}SQnbC3i=M0 zrcXfUCoH*AYUPX04aHmtVG79ovJlW4;ZB_@T+FZAN8}v4F_m! z+Ta@Zupyg$nU?V+5maSmX<^~xd5RF4X&s48x$9xjayG8HtE_Hj)s|4*oOQp-cc>vD zbl89A&&py0nRZ}df6Tjca%8!<;GYumu412@saxns@feS~;?!QwykTC2*tyeGH<3kL zmCq?dLZa;f+g75zZYO@pqC7n_K(g(iT|mj!Phf$;^1=$tHz+jW zq{W#oIuv|720}1NVPs&C%n^(i?k-xo?;7@7#@^$f+Tio9I+qH}&2Oxh)wZhL#UJ1r zZT9qS?;JU&kT{C@l7D$Y9trS{6Sk1rA=@9_@7)?31n67Ww$@hNyu^R&R8#X+Fa>A6 z#Odo6eF@oT+4N=s36T0cTkxVSxD01TA3Hi^y7YGDiO?m!wzTM-uwayNqgp36hY~V~ z_J1Zf)CMjF!ff7eVM9~G+$_VA;#7PaMrfb!TMbV_E*Pr?!R>?lE=q@RT=FB|?7o>E z3*sZ7cWo)&Z$@^%dkI*Xs_k{F4Y7Uf4;1GFT_!qb_--EXb9J3iKyrR`E)9Nlz2@M3 zd(n|4V2^Rc2l?~1BT6Pe9TEsK&8l*YH%7h#9U=gxU*!(Ec}+Sd9GzGSRh1Kr4C6_$ z0}C-uv4aRfLyo-7jUfDkFR>JE0XvaV*tJ_yCFJxyQKOmv*awtmy1{hxR7`@&{e7UntM@7|6=l%Q(uiG&}q+X_$OZ~koP%3H6$AcIFs5{Pyad9 zPTMU;M{b0kaz~5C&M@$%#Pz7%@{v4W2?XfmP8z^+S)D(|LVcU^n{R-fi@jkXraD=* z1_($$g_drU&q^O1-o2xJ4!c|CURGMmAt)_9iIIB zsO)}vru5F`D#MkNEW#@Z3s7JV*%%NTn-yLQVHso!^_6^iZtY+8_}zYv$dR>p;}FX5 ztTHGI)8*HE^6bVX+ZnJr{^cS^Nrw4&Lm{tn=v79JoV64;&(-XvWBk2b<%Y_7r|7$I z%qOfZ-KGeYur;!2!D(!G8#V)jGo}p^@62_&kpr9Wpd%e+;uZFPZ#S8>Y*1fg zvTJEqY!GDg_;5{n*%EWx#h36T%($bBbd-1mim^bGg_S8@G$SZ9sh*` ze-Z5M5;?}JmH0v@Gb5O}6B+eFOR6{b{H=QDs%GTmF($?~8iAJw0abZj{>;)m zB!eV!`Zh3F-(m;KmRS2E+}hHN%C$io?;!2?(H?i>{I@c=(nZveUV`j2i|2~9Ytx%w z7yg8c%d`uq@Ad5~#kxK5F;coWFqQwN=#GQUibYzY}v`c>i>C{3i$6 ze~ym-Ny+^e8(Z8@?QWEOsyfb>#VHA0-yDovhH(1wi0Zz;%u@WcQe*KC4;Un zFg?Mx5}mVR%dcK+akrAAO$*OC=)7}wf_WQ~-gU3{L|r$dzb|k-GZImCX0Pjl@Sx@o ztsHtAte9qOjHG;jZ;9_UAV_1a7tR$A6c{$`p@lBb9Iss8HC+(D@~9} zW7T!PtgTCOsQ#;DR+$({jG}1h#leNaax21ZEV9=sMVd?_vFSm(9Fj!`-{UCB>3SvDnVu$pVYMVBje9loQ?rB zfkhTZl~E>HN_cu;Z;n3IP;F#S#B!3M#I`Xzqv%+4?i3|E!%zbZnW@W&(d(XM*e{%P zCTh9*6xg6vDe52@rkyS}rz-wE`ylcP6ZM z4SK|Yovy}!P9J+er`oiFv8IL;wj>4RO7j3PBM3|Fq9*8KF6s4O59N$KY*}^(m2riQ zcHqYpOqr(8uz~k`q>~-L?_LoY_(%>h0lqX4f&VNn1r;-%!Sh{8FOSur~M4*McZ@4UTY*F+)RhPu2vNA^}N zQG&#J92~wri`myHCn3A!pe3gcUml|##92WRf&nG5O5NjUlFfNLw#VTMO-vKVcSxJF zZ=_GYA>E@{OTk|AGmc%gNXg!lt7xAwj_NfS&ed+&->S$K=A1uD@H6W(6>h9HgE&k* z*RrYV7=J6GIL6P&cX@o1X4!Bs%H?v}qXw*V_keL{82<3XKyiMCJUMyd8+HXfv3Qp3 z$v)S6z6%fOGEB{GAt0J*^=?_M4JPbIMRo5G!Qh=ebA9ISAmMx^Om>G6{inXkTINOfs!yQ0LETNR{TKc=VUUYS(fkqH0o?D~Q0Qpo6WYH@abt zSu8j97&(*1jB6+^>^mR(99?*&6Jan(+Z6rYKQLmk8YK2Ib-yfzrTS&YAXna;KjMGd)dC_^iy!OFZrd zdbaGW(_0%Zoo`d4(F=(r$xhIcx6RfwaxS_R9wjbju~-&G$wo<-&A*z1+!q&Rj0YRH zluDK7S<0u=N4OOCb$sLfX&4*an?~=g)hUQT%LP^sQB=c?`#5V?ZlpphgDQvZ^;XDS|%%-h7z{f^!N00X&cmqX3 zm0)HDbII{QO<^}!>y7>L%x^#1&JOlQX|!hxrpesQVN(#wgd*a% zdDfmsm7j8AKUOw}Nj{kkoVvj{oPx96p5F!cLl?k`LikG#yQ>dvj~Tg(kU}3bz|Jc> zQJvU3fHnUT&5l)w@QF$rIra!juaqKI?w`TW8*A-BV z1~(0==nplYqCb96VurUF4;h zIM3DyHe1IJMNa>Fh=$u2#}$(6rr~y8t4_stP9vzE+fnD&=($G(qg#gX$p<0kTD6`B z0+&`tC*TsJrTnp-qD?#WQI)N=cqe?Gk2#!6gpl#BzQv)S)E!eLR6iRb;?0$~MsXBv znv-(CVR_*M!u|03h!bXpbUg2;fuPTir{5Y@l7De8;y0>uK*G}~Da-Wr;Zo2gUDX@w zjBD+|5wWMxkPzdYMSBfB^~4>2Lp^oQQFr}o?wVWn3Piv=)Y2ORqN7(Zb&zAwJqv9L z?S}Fyv3gH>6P0d)ake3#)3c%jX2F1jPT)t{ETNpOwA-Di z^jf7aSGi`1*ViMYg{CQN`1K&u!DR*r;Pi)Ao#Kb(yeHYByJoGP z1-qU+r=BvdIF1}9pFF$WUigBueojaDyxIP&Dj$5a?S#gA+7a>;D9L2>ti#Oig~44v z_D5rTVQ*DmZXUKRzqY>Hn`_Yy;tF`eSh*9Ezfz|Dj7&GSbO;BmndJ4Mv$D8G6c`&#R0OrU zt#J%VG&!cj!khtdaGeQ-{nFuNW;a_E7^Piw-g&JT1gxDX+~r9y8$%OM0HZB5Y)HXy z3OW)N^?2nb)@6h_FMB{?(AMTWgb9L;OMi}>_S_L>S8Lt3qL66-B}7c7vumumKxXXJT# zjYyE1Mzm$PL@F>Tsl~NMJKBm9tW%v5885@JN@=-p*y=MHKukAkmYT2?x6O!_#7KZ! z8CSl+_rA{o!!vSJ?S+))2Qta}RB{Gi$%*NM}b@>5#qJwhkyMoY7M0k(~+yG01%vwbMQN19>3M8a#`l!n!*W~Qgh*8 zQycaSGB9AEMm>Z)Z;J=T%v0iP>QW18^XD_op~PfPt;mpnX0OV6ZJcD_u&HXE-pg?K zP7qcZ;$+Pr^g-ez=}3D8AjGL_s-U;lw9q4aPmfY<7^F;b9(W*e8aSIf5hqS8yyBV|6FM4v1mDvIFzf@FD0KID^Zwf z;si~cqB~OxL*d{t7{O(dw<4|!C~|zqygGh{@h;cb|2e8pm0n0SG0;@Fhk3IjLHx}& zjN00R!cP zWTR0L>|^M^;~{3v(2oU3-NLP$#W?QB=wvR4Al(#lvAm-jItHh~}F z*Tjzgs;nx|tW7M4F}fk!kSht&@2bi$x3pn&O@72ZP=do) zaq%T$Ntz(7r`)=QdIDLSE>m++^)PbqF;$hVs3)4O(su?@Obi`Z&vP!|K|P6bwqWO2 zc1D)sQDNoGuyd}Ntn<97lLsl*b^q0qQOfX)1T`J&nNb>>;O}|L);)2hrc{wr~cPCfB2BErL=F6lO0o|@`{w z`9M2_H)ItH{?tC150f!NzH1S;kj^31k}C&{Bjx^%j4LN%@Oybf;nv2c(=Yjr*VhJp zXUFjc`)CLO4b1TSxanGDGmL*zU6mlz!fk6k0{Kz^6fWit-tt@&H8`fvVL1rSu6XV4 zhdxi+PvHS2zkpK$W5~GuTIF#%0=J{!7Y*itjx3>6A1Hw*~d&~nECaaaEK!eDBMlq2= zu@E{NfJrZ~qCnF?2M@xR{3c4hWXMqR^=ip>{J!~ERl2=-Ez0w$?3D+}n`fJlgNxkW zw*$t4Gej6WLB6FDu{BrJ`CyJCQsWMq8<^}EB-CU$-bRG!EMrWzhF!>pw#hprb>##Pyv^HeD)bqYnT5w=<)~CDdA#!|!zwAR; zuioa4zOE3)3>A=$NdALE_mc}YURj-AQR}?|4XE-1S^^H?F(() zEsKk{a;F8G=gz96==3^PQm?-9{i5ncP@)YR4dzD|x0XxOHJss#iGK8MhKkEyFZd%+ zHM1jt*#j`Qp=-1V7D4W$>cGY+3|L2=szyPfNNvMacXglzR=_TS5lv_F_TWKp+K&Q% z7zp$padt6!SOQ@8Kc=og+(VbtG>0I#7ij9VqP8qhhGf}K&DUQ8v>m;E?unu9FwwhC zO(kqCed&)hV_Z!YZz4gJfOjq(u(t}P4SSz+L0I3 z?eWV-&NURR$7{5Y{@`;}BT(H^Fs;Q))0)N>#~LILwf~hFmFpYO%KDBB&?5g+--q@e zGNb?5{rwxH{y*~q2@8o|+2ecZSA$ornl&_4+qbK#G*~LDRgeXYsCaW5n^qeeE1et7Ei~KLE+1XjK5q8Bk2b#*ct@X8ds!n)SG>36e}wB;LtxNp2i>{z`uK4^)q`z-Z^&UO9#VXV z{K2;&fMh{$kh5S3L;66YfaU$j1fEH|PwrUwf?oAwQ*%Egt0p+x&v5Hi>d zDSu|bd<9ymu2g0cxv~V=Fma{~qfs4!BJieb!4_gdWh-+XkpfZT&mO6LC z66UeEaQulGAuT;3D+7MYIlbxZjM_)q@$a+>5g%sUi>Z640%gyEfqezY-UMFv?5aj% z7Miupy+#=iX7ei3hh~M6i;?7b5dI8OXk~Ok9;XZ1fkICMLU3bIr4n(bSZV3}>XK=p zSsCAsfl@7L>}h#4F%i#N#cP(358qAMRj9CRP6{2y?rj)xOt|wIB~5Nw+$_Al9MEK=DIs zfiz8O5_#%m!~qi3i+v{VIk7Fx)Jd@iug*zwK! zhw3&{+5r2k1^k<kkUTXzaflu+-ya86B0dKfwuke8T5A+iuAkCW24*>nPm<&~f*$0lpwu1|EI z!rWVz*Lxx(BEqg4KpoCy(m_!)NJo)WkWLE+9vB-V8*-;FR+@hBsW1DTHa!`aWi_LA z1x{pekN?4?*J>Z&RFFCmx=;H$HS#1DL3~qegY1)?Wb#4E=zQtcOgy_*B6+ajqU2*@ zeD>yV>p1{xw9uiNQ|0V7e065ra`TMuwGj)`Q8)T4P1-)9M5(88#?^*Z7&RI!a}cNA z?DgDoZghvZ%TPq~apFcvrLj$NC728y=eUk`Oi1xts=&hHQCS%)CFMZf!Wt|7phlIi zu_x(Z>>Rsl(i_K!gl3Q*D$S`C5H79d7Y>tZKFUnNc0=H)M$h7NQ=~*$Cuvh%H>Zw$ ztGPijW?;;;&#q`9riKxPH&DaLSQ8n=WK={^Z`4HKAe9>KRircw;m2_W1EZT^xPY#B zIO3=%lkh^y8$|`9t~V^8C=(}vgx8l?q$J)LI`LdX!J=S6=tomDKSGGabIekxbHGbU zhGicXDmzjdlwm^Xj}t`Adf*74KY%`GIgncD2b}&NvjiCaW+7OQBq2u z$n|p`{915|&pAY&@?dRezQ5n6MRM7(Mta)TL3-Nzi3EqJd9E5lxn)z1U&0Am?w{Ou zR&Kdb;8fl*%~V{eL%NxL9BJL?k;mdplg?K4LMaE7VmH5P3oY4Jf#cavL#A+hFzU9a zxd(J2))5TBX{eNlq1@k>h}4Zym|tn|Go($HLN`#JQk1*acu!T`xk(swwK5Z6m3%RE z>{4SmRtPMGHJ`AMy{r$&o*S!E)Zxeyhms#DiQ_4Y+jrML6*HOD*cmz_&uPqIVa}sK z#R5~P3YXU$73Fm8KWQW~zQu7@?onp7&!0aoq@*0`kOJXs%@WF})brf-43E}RCTJjK zw+8lWYBVG=63S<-oaVfG;%_6~q)QUB#8X4sur`LWam!7X!aDFhK0v;$Kg~jD9Toj+mdw7P)-zv9_f*q=rBn**Na@yKe-mk*MIrpMAWe&b@PgR8JS! zBLy)%p~@842|MCrgrD2L(V{k?R9bK?FMB+mm5BTyF)DX?1aZB%B; zs-m1SlN+>_SLVC&j6tYIjw2t7nv(3+rM1f!8yg#3>OmtmkJXkoW)oqu>~P8sn>O^I zOjKB-ZAKhu?5uj6Ed-oxJVZUhgXk4$vSSQ=jM2&(@1jp6Kc5F7)S_YZ&_aHUHuU{^ z-q`nEQC`t~)O>tGd}?3OHZ?WySxH{j^$o1Ve*58ui<@BkP-q@)3a+5t4kx_e^tN3} z`*i8w+_v5f*L9i(bbaM-2k#pxNN#d|#lvPT!%)CBL68y% znu*3EiXvyxl_utk=4eUqAZ^8J*&2&yofG`nhuGyVD7E4WL$M>a!_ZSa<*%i9u!@x~ zb>U$x^nO@Pa9fYJl6|ATtF<(Ncq+3p* zEmUH1BKLN`i!U*3j}&1H){WzIS$QV0YmL+e>3osT+TEe_Z~}vwzbn_`)!=$M<3Rd& z+>Ly+E9%=WL$!L>-WjN}m6OLB6!)vebdH{e9|!1N$>|uAhEsVaIq(&Fv<%s&qrWYIv8<}IhigldBub7dOP#1(g|(FwmCR=HknRNEp}m& z?e;@j*{ACVDj|HAyoTS~({IrG9?#)7>1RVH2?l@Pd#29%EA0yDvJLoTWgpMr@dI*A z!rt0Bb$BJyiK5#v{`3Ih8@Rf`jM{~P*fF)|-mKGhihJAux6O+{Vz@=gsf7|Ksh>gruy1TT84x04)=)KthG*t)F}MjSjk z@90i~*12T4=JPq_ARtzi_dmRg&vFpT@3Z|gX?h-*&*Y3=d}E#2%kh96v z3r{U1YjHP5RfQ}^ZN+}MwyeO?khfs9xLrf7zU(28Fv*#eS$qem?+7v#`8fuarvbnI zr<`-(lEZ?9YEjy1TV4IrUIup)zn4@2iO3_qEluuXuH72*gkA{Wy>iJ;jLuD`r z1RE5ZR&xn0tlt^_6`cmwG=K23%wFD3e$=(*quVoAoJrDEbyh_m@uU8ay~2+I zH@MUzWQtu_X}V0U0vPK_VsLpZ;|51LJ^6h0P8%6>iP3>va^i4V5jh!QAoiR|BdOzs z^$78W%M@cNqGNXX72EamerrZ7mvJiG_!ieO4Lb-Y{RHaariEBr$q%l|tlKoL;N7v3 z*G-lP)%Rj@uSQLl@sumH;Uu(@Bn0IoG*)r3)+-cfW3;vVkgZC*q|>4 z>uvvBPm(zP$cb+T;o<1zrsa6NyuAJC2%tNhN!m*;Fd zcq^=OQ`p>0<=Jv$b&tJO&tF#K1Nk;m+;wHEH^*3Sr?lgpZN{EXo%)7FE)FzAg?(e2 zIdj$-x)l>eYq%MS(d=(0wocFXobU5HbU8%l6Uh~cDZS-7Lw*iCqX$qWF?4wv)SF`NmAEIrvrhxiE$#WdgAd$w%^XfF z!}2>{zlE1Bzh0&xNIn(0XYWV0sB4;il{4A$&1fjgrG|yJD2`m#v*brk-ZE|B$9xZT z$JwQo49U+D4q(VzWTIF4qEUqLB%3-#pP3dP)j*&pF7S@#n%_8Xzw5QH%s29`s z<3L|f{~AkRt_pnu|Gr%kCH$uz!~bF|LCo0FR_YrH{@-neMCCPwIXPtQVrg?Hm5pI!R#;{pJA59>+5Je=)2qUk&uo}-A~GzOm6^#t4v+PqgB zIQaJ&X@!i-;;M5}gX|*t3Xk_EdcOfM19ad_fPU9{hyifEC);4MUDrCaKB1ooG|Mh( zbUZfGfJ?Jr3ha&yI1@CC$^rv*JVC9|_y4xCY&EIMOr_8ul`BkzH7F4UjX_+kkNUKz z@9!xO;0`8CR5kk~6x2vqz13xq`CQLak*`lwnRjw|+kE zV^5UEEZ`3qRk?|{x|1d5UkWLVq8%wFEt$_q8{Wo2{BaH8%sWOYuT~X`<43PXIp=oG zGz}d^D}TN>@tzgNW8@VkO%DT#_vsG9E(IE-(xSM_@e{5zt7nZS62*t%V2I==5@=FI zH@f;=vk9_Gl=?)fs|ZByC5OQ#ey_VnoD$8SZeQ z5HI!r5PaZOO%HIHcPcC|vdnVZsrc(AR?#?GxPP4kjTEbtx2YQq6U$P0YTOHo=j&wp z^qI<_y$$;?C8w}*CaH$-zU~|5I0y=5P|0>9+0pV2m}=Rx#hqFcIJ16c{i2UP0Ox}+&e6x6L=SgJhxAU8aH)Un1xdq z5E&WW0*wse0h&)?eK;~(iQ0RF3M9;lYKQ`~u@2#ovPBG2JGht7-YakKSPw(4_BOX} z4hL5BIl=37>tCI(=fNS|N7)`)Q_i- zzin+=-{au_Z!s_3|G%v#`|p9E|Nh|qB~ZaqMfr@P#nsGuCTzDhNS^K&r}{?8%mSO8 zGoz7JY8#R58^i}l4$i8j2{lKE5CHx($(d0UJ_*1SqAeO6kifx&a|Tp_Tu8Q~^7*rU z0w1i28mcE5tMvI#W^|fncXro5H8&q-f4!`2{fyX)MmQgQ;+hV`8@8`SLPa$e#~L1< zi<0X!!pGtdMnBvRIgATQhS?V;A67^xB$iZ9qN;1z?L_3q;$CsfEZHri=1XW^GIHre zk`?bSjzMk5LU;$~vmTP||EvR-#d0O})91F>E(Y~3g12RaG#AO;XslMkSwg+(5Lhcv zbnF;H?yXI|Bf5jkEmas)IhNrN_NdZOYuI2~G{{ObD@$m)y-PX^Ij0iTnJgtIRv=+Z zZx~hKG$orW6v$PoAf9tGwW(8j%urx(6W}v}Ja`WfAhtr0{q`7Z@64nw7n!1I4b_uO zA~&iq5XF!xBv+KH)SYs?7>$;?GG$$8=!|5d?-;MruxZjP3(HQMw|6LylgfHjDULJR znz~QK_C9xls2r;~$u4WM_3&Cm++DbFN@#UvQMi zl`|04?ISZax>KHqTXk#=7r9ktropG&rzb)SWsH_`61b_B8Vu^xmAQ@FW_b_?!@ELB z&n3&YJ;pgq6h)g=If5^i}K6P)GD?n(Az}lIrh_ zTOYEds}EKa6&9q|Mif6yH1J|GP?qUbR48pVy4}`eVb66eD(yn834M>GxFb?R5&B`? z*Zb+{Eq2{Ha;E2?r~Bz{{6afj<@Iw|Fks$C`z6^ec9&dN`hUWY4<}!`AVu%QgsRf( z@6t!5yDB?Z?>HgZ4BMlNN=fa-__9fNko4LBUvQ&Jy`$P-sj=R0y>I^IBK`d1f)Zrj zHRJ^uiDt#6h*4d*-0$J4tADgAY;xc0Mr_9Sdh|aQ@ST9?o;)ZV$&sbo+)uY?88;uEl)4ZL~5`q z4Ag5?*KNDD7LBaNEbj9(QC1I7F`wavTJfT?UA=(wOjI>}#MAunF16b{r7JnGIuhi1 z4y?u6NBk8s3cvFtqjU(KP5J;4VULsV7=_Bu+rHpiUiR}5L zGU{$%hoRca3U+|Ow*oDeVALv*lb8NkfRKEy5>Quj4K zp++q4Kgb%P1NMCQ^)4u_G&l-3QdoetBI9OG@II0JC6=K=WoH^A>O;Td(OA0tPvkyV zhLn?Da8Eog)9n#ngd!9ojV+q}ykTA%*zmR42HmKU)6xNckt>xbj}YkCYouAkNHU91 z76z!I5VDsZxmhbv9e)`FV21hrz;PVinbO~IaEkDWvO7}UU_h^c*pQ%qb{)D;GC4qn zY;y_O!hYPhi4^y^SMYOJ8ID{+To#1QJy3T`sn_5n?n}WwoKK3IsLrt1$dLb96};%~k1qjq5^pdTyp{!RV5 z2Vvcv$CatV*8TjX@vJeJZQ{cx$jd$C?TxGXLJvoqDvC6&I@33mNpPr#wrMo8bx5ss=;|igmr*nkL;Bz?K+j$TY%BNlPPt8i zS94y_^wYg1o5nWFIr{b-#7kwTo%8nFL;J5XDyKKGeEMBNi~o_R^1mpf-+*ajYsddC zqm`-_w*M-lDjMqE6`9MW8SFCJNBopybgM96Ef81Nof4a(bZ8eSfcmeKF8K={-$dNXDw!^qIMqbqb_5H-i%&9A=g9O_wAZ1 zymGM1ZKJt))qFi}av43*@nWm-D*p~$+pXL{qqK09(;4w>%v0p0HWO*;e-8q)`?1>)Q_Y%O>O+e}<&ZmRA91=dY!^lj?)Qvq6 z=j%EhT6$+M{JU0Ptm!*jm{UiUDrR~wWFi-B3PUFy>^FN{f)TAQBGALKA#PD1EqgK} zpHGv9jg{}J$!x8&)U+XCqIiM4SQ&es5<5(;#!_utOeOddmJ%DKVM1w0TH0OoR>ND> zJn+yjrs*@th(v@;v0_O&J`Mx<2!giQ^5gHyD-l>4C`yU;NJ_4rd~+(jd!33Q)Q0ZW?x(rkChjv`k2t5f?e7UgCQ}8Kp6F&*kS{8Dz4nN ztolevY*E+n`{PC9cmzSPi=DD;KX9>y6szIjTHAypX&eJJPkh7bKu*|kLXO`em10E> z^_57k2J03Fc#RhMd zon%Zwd>s){P$KG+G{FVi`86g3qgN7xB`(WzC+9M=WS*l|^fz-xFlkYnKrh{tCy^1c zmIvS!fN2dq$+%l|eqE#AIK|#D=fUhRm&*d8WiMIkd#eyb%dJIKhPQE}9YdQ>AiXF;-7Qr5ML?NLf^9G|1WEftQNEFoUAMz!1y&ZlS-OV`j}@s_ zC%NvSzH^J058f0l_Icw6DOKNyrJr%G-Hvo2gQ42gAPL{tTf91zxg}E}Gfi3izO`EZKCYUX=x@a;Y1X_g`qjcUjpf3%75Bjq0A}NxhKH)o;r|onn~w}* z%CIw35OfrCx0exgw_KTv2juN3+UIi95ya!lkL&NPdlOKs_X0_J-M;&9swGjx=5O`W!x&DZIFWwr%=^pl-%l%a{Wtz02HXQ!&aDm z4_ogJN+gW{pkB#sz@e*v=@B!UJXPhWTpw8=PaQrs@q+{!7}?@-Tn;fgHv}3WVZc4A zbCk?#c2eE?`|1Co35Fa{-7i+!0lih3DQglwr@8)Fpm&la z=A-L!^y%-F+1~sJjeWk}=`}?Qi##4}-WK(^BGKk7)Yfb(WIC}yx+0#rSdqiK%(9jq zXfj%Xp1@llGl76mo`$Pu4;DJGU!qpQH0f9knOV@vMUiS@Ls>I6>~2nmhNf{yIowQow6`M{3Z`_t*yDyZ&XmtPAs`Zj;rd`Gd%xLxZ=&O1zh z;1bKe<>>RNccV~UTUYcDF!PgD_kqw=^dMqRi?0+i=OiN#aJwK!9FwEZ`0~E?364m& zfV2ZD2wi?Ig~)tY`BFJo1Y^^hV`S#k{Tj8NlV2@}n$!TG=?t1n{V*+Jt>P%fPGEiu zrcULHT$@GCex()OeS^p)C;h^U2J9r)FwdrSlY8`0JFYT2u!(M@ayvBtFWk3%4N;Gt zah0WU1Gt;pUCbx4CBYzneeq}LsyYrn*lu^oG8g|-y;wTm6J5{UuGPbVNTiaDGhjVQ zSV?7I&$&gO$5+P2;gCl1y*pk^2R?^gev=(R=)PbK&Y=@uQSdAI{%bXP5eyK2=+}=w z($&5$Gn%*_j#T-7CB3+AaD%EOD$i`ECrSh(poi93my&sV6-?bPjbe{HL8fk453RlZ z%j2F$?n>79LJ!jCIUl%BpMp=xY6*kOd6qSWbo2Ynv@_w4D^=kjAYdr%TLuxJks;h~ z{cu%l80gegf$M!kA-RPwW#(p?9AB6kZ2fXsLfI!*u9vvrp)5&842!W++?wKOHI2uU z2<*x986sji*l#EIj|(|CFatS;Os2*ss>f?rh|ru}?i_>&kDD0|RViH&%H1_o#H{pk z*?mo_styO*2B<*%Q4Au3dZgFgFmqN--}D~m{I;Pf6Ev6R~KVn{v9&6 z`f;^z{w;M?{KuHlKS^Ex6Epe`x7F>x`V#+1nX7#0YMP>a>4@3K+pg6$lP8@NRyL6A3u z1l_(7J0M8Xp1eUHL>PsHLRZCZ1&%3Xqhwf85>}b7YWQW$N^M}_N0;Bx+V!t4N>yE` zUCb`pV6+eRy&*~Oeq~Rhkl<@+!1=+CV4G4`;k<528XwI87vz8;L{rh?A*Q?dQYm|T z1K!M337HHf>5Jo$;aBBmnXq5v22g`iGwWX|90 z3^7v?vRw#}+MBr4=o4d}c4C8-*-^*`!xcX}Y86UNky3QY)WSOY%JTTfBvR(1UWU>f z8Zt`txtggEE!Kg&T1raDNA^!;ntXUksPMEdB$1gK849z=du_Sx&}4goQ4C=Qd2Mxg z6A@idax~iGB7&#O+#wCd5X?|ZIFC1Rh23U!4iE_ie)@7W3=tJ2aQNtR^qUBP znt(?5of6$)fuZ!@w?lL%XGNi&nKyr8>9*ABs*pV4f{q#mr&-*Pd#ikDnk7%!Pu;FH zxjm5q360+Q$~X?f75$=&e#&bdyD$>DyRahI^MbK+`G`c7Ia$p%iaq$`a`)Ug>cf6Y z3fT&iKy4%xvi=Cb{$20>q^yOAOtaZ_?OTNC>qb%XSYAqc)v+64#-;Vq6l8c^#x4A1S@Sdo*@LC%!KM0wyRV~(;Yq1Q8CpBzA< z7Ik6W6bJ}g59sQJe_T-TK9by&9;vOArNZ}$VV?Hgi{0-WSc(x=cV|Hd33f61!cYym z-Ne3Y5$Z(hu*D#0LXkhQryAIfOFgeEubeeb zY^xxd8E{06sGxOX@x&Cgca`q@;m!h`r#j%m;t5x6alXg^vo!?A{0`5jaAOE#vrT5y z5X%K4tFOj?B~#1cy?7Q6J||qOfvQe=T%z1YY{}#D?6wU6#5D@+&g%(A(3jn(>68FcoXiS0eLo~<-;R&`=iK0Xv19$WQN=e*^Pc5!IwRptZS|gPk zdjUhG7E6=MXgJ&k!3J2a^M8fROG*Cq&ZHN8X|`!S%oA_O(uKIx;OD^Q0{;Yert?W_ z^jt>3wX~lnEvC?hT%v?VdD*SS9%uO~!Bt>cAMX1Gu&gUTurGBNyzDII<2ZT9=SszJ z;EY+TM?S}sEK=OdufHoT9Z@o%WIFSfxyEx61-Ai?;M-Bhjs43UE zu0ynHl%;lWt_4LpQY>)dPMxeNX5P$C$8(In&YugG;SSS@alO=FcgeFE>k9Pf(ytP% zgIK-G;i?C*UnCzZp1eVKk?rY2nMQ@7$W3~zg=yHV@x?tt--qVzc@d9_q7-h82PGbl zfpWm81pdXDAFRQ)9s0Nxz{Y8d)MfTHBxlpx4YB%@ZfGvTC_&D2I*J`uZ=4n0>9-b; zJ&WEDsVZX_s>cHcb}@%AtNBvLWV*^E573v$((ikl>nfs}pzFV!ev`D%yh>9}t{QwL zT)XwY*|jh(9H&lwJm@_TZ(U$yR(+5%JAfsvi29B@#I-MdpzRkLCATxBOp^v01$BtK z`6(s_TPQ2YPY^4@Rlb%eZ5#b5tMjbLphsxyoR93PEy1jw$9VlIPF(ccpiUbiW^%S% zeOYQ@e#TPjunpGfi_W(SVQ&XzyH3}bPrdoRgi z$J~bPA@yp%A=R^J34=Ruh6!1+f<}cN82_@ng*gSRP0>gkX0SHy=}2pV%@zlfrXIeUxNX6OS~avmsd1C3{nkXn&Pm zz2$D7$x*9A;mR0u#}C5_>7|xohY#~q{0=T`6FIK;exgFVwQz4!cj`}JW}UgjEpQ+` zRuL?3c^lxsP2LB?T61t7V0)Rj&*zGiO6wk+{h*F>4D71f#jn~0&pHbC@r}O1M0z6p z@CrrllA6`&A=WWA*X=`lnW+D>q*k7^@%a zYQ`Yg03em>f#zK*cO#C* zZhl$c??4a`JUagn5MK@@%=bj)GT}^egZjhpxX+Kbx4eYvs&MCcHGnCZoODvHGC%cX zzu}tSAurb=6P>%TfMXWxJz(pO8*0Y+>rMl&&FHlQTOny*Y#sgG_pg=-*qFP2_?MiF z^>5{5eE<98K^fo*aC8EwI6MB+Q_|kZ(a71(@n0cdSf8$myK?ePmaVT7pwJx`mg&<4k3gO!pe!lKK&u6~{WUz!7 zuD%xPt@}>T31KABc21OYJ6uS6jlaTyvuWfvjueKQQ)ltha{L0t?&yY^5DTIlDY^Cs zKDB`&T=Lx#Tbq8dhHwbHkn|fZxs85^fx=JETqjVOb2ukoPofa<2LZ?kqpeJ`#nbQ6 zwgV8zg}z##CA=&~=5J_iLR+A7QH?IIaOX*Ip!5j{7PVJQtBA6R+jNmkfssY2Op&ha zhQ*`5R@|7j5=_N>^(0mm5}eX%Xh<_G;%&EI0@k+lP!uLTWsV@*%Q*{&=-Vx8w`=xf|M+%s{AwuwLyUk(3B#VNGzk3wPj!%)i2`UjO-m9ScJ9d*Op#4Ef<$$mT!l? zM#}W`L)FDs0)GhKaDNM!iAX{tccfVLr8~Vow)9+inGrs}*BJPK{|c2MmJORqsP~W`xM6~m4UJ3MR>d+(GnypAAEu+TV4?J!v8o*~7ZIN} z!HpB`24?Jy?+UQqfIwqrJl0R@0&lwtvX-}Wml~J==@Y3wA(Qi;p3O>(!Sx{Jwi-_S z$Jv0v#O6gpLf5&WsC1W5us7<4ow*ZWTC27W@JDgZ<~4VTWw3Y&N9y`ki?0g;#zT{h zr+6$SHq+n4oumeGnDAs`WD&1uj|VUJ+NMARzTC{Dwk zA*Z5*JFK;b4GzV3LXx4I{}3>=bTuGK+4>Uin%u^QmSvwN-2$ zHfd(UhZW|SkD50PC;XTisDJmF)7N^K2*%0EDE8BIY6Y>O4tC@+Tihgi!yAl@p_3Q{ zN_AVp;&maclnO8!Th<Og$2OC;;!Vde1nevs>rq6(F|} zz3l8_u@0Yk$@_ueYc#6g_mju@z-#B|PY--o zy!{|LMvHs@7$d&QT~U0(U1RuTXRiPSm#oyI++8PlgOgUjNS^LN4W8~{P9B23LY|y{ zVgHUAofWX}xGR z+6J2>iw%u48L&|{PK_*tucgOWC~_ijTC=FFW}>c(KrGQ~l}cDD0%=QyXr(r9X9#7Y z-(b)^k;~QIERoP6cvmiW%%hA6dw#f8jvPlq)djT^CU0h254?fAoA&}0oV&Pt&Ia2G zJ&-jk@`vk})*6$a3!0bS@Gj!tgs-r?@o_Bb3;PY^;Xo|bcC9jMgU}0aqcmQw%s{z3 zy+!@WLC3Xi4=VWyMc5V)66S-=Nw+(^t?toGGWy8;(e?_a_D2flP~aS3oye8vE_iuE zb(cf)^}b2zBDk-3E(x25?fE_AfC*CJ#(9TiBK{1Mw6{LMKc{SGV`anZ1I~GK{K`_A z*DqM~W_R&ocqMOs{x`fD0h?nlH3L!ra3?RQj>x|}V5^{wbM+bbh6p4EA4 z)F|2VZ;hoTie0L8W4?2<*^pzbHMT%08Wd0hFK%du^Zqyz>=96RMWrZXPJ-_!0UJ%B zXz#kmfyxE$F;FPRJ2V+FbVhd!zL9&Y6m4v;by4&#X{t^SR>wY6cTr^v(**<=Ys@2q zYv^wf<8Kg51qo!Hv~Lj7Z@;xn2KbQ1Vy`flM-@(V;~~4SjHj%t2C%3%=!j0sq^05i zcn1Q@H$$Dna6szAhM9i0tl%UHH-e?k(u}rDgnpl@=7Dc{EoUwfZcm`%yBb4>%%#7jzT^azR zcq?dLygPq%NSFSR}V7%GO*C7D348iQ_KkkP!>Y$j*7%TENEd z0Ws@aY3XPNp*vs59y=TA9xY(cLJc_-f7QsJl>xd>Xs(yTpnu7jz)I|IL z5#HP^oQ=)@l_qI;80v~)f6CR5t?w7x%$3xFp8~_5+QwisE;&kO6gjiYeNW^l!h3Le zV)t-v=-Bk8rdMq%ju<7@01^|1CL&6ru%Ie+oFE^i&dVc#A}HH||1f>LNFHeZsB74M zJ<7bwy5eMw^>tqE0{R^^5@~$R4u9-v{D4cqw)!Ej#yh1up%huPo|j=Sx=ugvY6 zj>nKXfbMuC%?ZJ#dpN|VTeXYBNMqlG*G(R3_i9hyy+7pUkgaRq6}s?5_TWwJ8<>TE zzbn2$7MjWaCdA_yiPoxy(Bi@U_A7MVp`qyV+6eM8DVLWWu8sLzj9vU-l7XA`_psir zaf_;B3*?vX!D2^1YLhV(^5B~EbUF5LFwmB-XxE4_We4JPk5qs!Lb&ajx%|>0Jt)`w zYo?G0l2eo(@kBXbDs8AF!?&tpM;2vYqeU3=^e|*x{>OfSr&54awP&1IJ#989olnHj zRORkAWRX7mEJFu3rHPHR%F1A|lg~LzNNi$k*tAfHGR>g)ON#B4@#_aIc%JoI$$N=Dpi38M!;ShV+jNKkgnELP9KUqY5yWPQM^#A2 zHhxd>ZtT5S+}~gGlwWGQ0kNW#^AW8t?U{*km^DEeq1b;9Clx_;?>3C~`^ike4DhK; zTV303*^+e~#~Vi(8W4Amj&D3@8K6^=LP1LDjpC~jZBA!9Z>?;bxNM?wI4SxVL~TZo zT`W}@2k#C`hoPh&0^BAZmH0%e<;OR@v1L7j9bIrkCLum)&q~e}m8^P>h6%-!R|RPv zB{`N*>shy>u_~NSUgEzj=B%THn`FVqTXH6hS1CRexwa>y*o+UdCs+-Vu#CJk+14q$ zayZpvb=%7;=GkteHR!R96=c3Dm`dcOeFxb(qBl|@*9%!OUTgE4_=y>1g-IPR6X40p zDKc1ldRX8cWiVtxhGI%r&jYwu9AMt>FxC2mcsv^P&KD?WHab8#j$rxbZ($se($%J4 zK2BlMi45G8>L&)ukq$RKM1%rX6F$ZKe2>6UU@wMG@33$k#l17)X1)5+! z3+>2%I!d+#HvZQ|Z;YxtnM%5N>YgQbMT@A>xueWuA>*c)i*jg;ntO;%!aQb#_!Plc zvK(vO=fY-)-M6TP7rBg1R34K1+Z|&@Dp=5X&+&UO(9eC3PNBf*&cebNq?|?-y_8Lz zR2gw`s)4aoM~vT4`BEdt2erxwjCWGy$sB6w*5T#=m&uI; zrU8ybzzAms^XUvEk3f>o8@#XC`<&J9Kb5Kx`U`+Km62602??0y5Z{w$%8%mx?Jqwg zU$TdIJb{#fiH#SL9UG9{EXy35wr?C{iNorvLhV#2LmBp`CnKhMNny%*=Kb_czBRw} z4EqQU=BRLf%haZSi|f($-=~mB>f*l`=2SDUvd;PA-8oRZxzb1r{RC59LBX|6X}vQZ z4hFb8YJMjZ;k%PAG_90?sonM9N-pVB&NHp4G*gtcCFDOTciybw&8|E z!)@7l9^-AL8Fqqh=n}73-1z>}dDc@y)*FrFC+gR2r}0qEBZT@02jt2zqLJ3Z#6wuz zy2tw)#$V6A_;1FMdJT*`2grosCO^*N#_%Oc2aqjiFiTC!Dd;%NVmAvDy4rdxx@?No z0avyJ>cvyS^?^_3A`}IdEa@fp$Y)tIVB+22YdSdX%S}ygA#2*W+F%r%imjS@Nc+s| zwF^GeRF>w@?9Oa%qsJiwrzbEj9Mz-UJO?r*_QM{(pzstop9Oy?HYiVdLwnM;0Hm|k z=YNjqE1CQ*LA%8wwOUerzSrzz3?A>r&zl6;vB zqkv*dWaxMFAv%B>OEFqkY%S@KFd@HXqpwT4@zDlhCsAr zE^)o@XKTS>!&g0z|XODJ~7`{fA3B>r$T` zY1!iQ?>}aPShlRzPGmTp+w^Kv1ca4{o9M~OIlspj9AMC?Cv5~Vh-C}uhZo2XN^Q6% zXpfk)iD7FZwiNV7O%_rm;x0{?Zy6QQU(YUEif@P@f)zHKN9v?u_M%t-AG5TDmi0G5 z1Z=?HLH^RgU4hPBi+~LPUTUwvVwc~XV`Y6#rQgaIi-UD;GL&Lc2{js)S|J^C>_B$*Yc~VWs;LJGh!~eS)zU2}DY-u;}M7=j2TP&Xd%j4^M6t}=*ju~Z7@6+Oh z7fTmO#7&0%z6&#|T^P5!3fOguW{}$;N+)ehzh^_ zCgatQ*f(t%^+z+Mb1CJ9?3+i-@ai~5-bMecTfs8|fazG#Z{HQL^Yat@uf~wK21NcY z3=mKS=6`JrU1(pjeI?8P`T9HCD@TCSKYBupqOwwQ04Jxfo8=-dwkFm91Awi8nhJyc zf0{*Bsq@<5aUxxUp9sos=*}TI&=LicQc5Drgb+yWhqUl|$^^;SKL3(pdfR3dbgCvZ<_6XsvW>M3U^;z5A-n5@ za`L@<4VsnO)Lf1a|3>j5W3l24L%~Bx2A*?*396s)xr0zAiu#T1dBu3>LkWTOXAhxN zoD??&ckqb1MbMU3OalvKnvu(R8G|bbA?@H=qF(G8%7vOdB_8(g6BlOewL7zPXPcZ|2K)TB?_5yx?mvv}~zKxARGu40FawA`&L4}s?~*vx_~Z{ja0JYa2?4|Zt!k%8zQ zU;3o2Fr1sh+;)wbap-e&QM)yIYzA11WEm~J5tM!RA`0^^y7RjVK3$Va>b&CQcyGM- ziIHCJY${kPs2@g6#*nA9m@R!n;0!Bu3QKp@_0|m!#-=zrNOg~i)+5Z(2Q9_Ve3nM< z(v!J0bj9Q2^m6wSdnR9{SE@+MBd~WRpV|!A_2vs#?(2k7jj2o&I(#QX;#HaUo4Ow} zda*P6-F71GDF2=80&zgjb1f)7nCasmu6p#%ZTnYWJ?9#@|4J|C{|~)zeG!eFqZy-- zy^*mwfYI2_#>URpiP6~Ff`#!v=|%ZpJR|z`k+YGlv(rDT^yCx^r*&1dkk~v$C77i8 z#?ypmI-6A!s@ItB6X9ia;Pg@x6lkx*O7X=+V^ZVYSFewP&x3G;&%kd~Ay4B8UfEpP zMl7Uq_!@rV}hr=og!;edTRlhT_Uy z*}@TUQUjv%kii{#b`LDDo^n%8LQGN2JdloMgxO43^b$^4FqbL7l_|7%dAkL)M-8PIw;P$?RqWzo zn5rRc6G&8^PX_=xns)HR5uhMXw2#?UO5Bhuw0q6lWv`?fd&)B;v3#c+@HI-F$az*9 zc~-vvye?hUG^?xqekMkgE+C}NHPODVTb7!a{yQtb>9?2{)$BQkl#Ck%JK^pUjT~(0 zPtJTC?3Y=eC=sfu3V$zay?cE4S;4;Gq6f;itgX}=MyX#-FXfWaxCVya#+w-s^ zJH1qw#TceMha!3Rq=@F1xt68I>bLqWTl-aQ*0obkaq+T3P<|?OSLWN1^Qf5JW|iX7 z=TzkwJA4PNh z-})zESVr&49Y5Wty&ler#zZg@Ac*XsYSELI;JUgec}>e;(;oOog<{?gMekJ>EA+() zMbG;&hdlg|Es-Gcgn~3Gog1&Rb^%_UWVq){iZd)FfbBpR;c{$Q#4C@0IiYbIYANs% z$I3ae56IwEoD*h-T{(q(pm74n^+jP3ip4qDTb_9Vq3!3o8x`7?-4QytRtN*yV*sC2a&lg&^NS)L9G0DQ;!%Impj3AT+&+OF*ZjF~%S633 zrxhnac-p(=8VC2rB!YdiN(~p*1VCCb6-cBlHwS!1jdi3~)*4yI&*0wQ7nSiC3W{+& z&Sa`{w!&y?cO4RmXl#SK(+)Y{-{0IG^}qtQwnryFllV;wkSQHo1u;Oy$P;Id3w8tO zbCL~;^mzjh z(jvq9HpbtvOrcTsWnS%e3Vxt$M8z6QOd2y6GK!=S0+`0XL$E<_6yogV&|=*9tL>58 z+cAnhnXVYrgnu?Q0MEhDPb(?zLc4JKEvX)>#13GRmCfT&_^0TcTTEluO?G6BtiIU=phj2U~*n1Kz=JD+J$T8YyZ>xYmOr&Q248V41)M~VT%3V2~&T`Z2|u= za96i-niogoj{tBpM@Zo%7@}%RTkNeECIW%O6&*kW#}|1t9igL%Ie;~zk)>03zXg8) zrO6B4HZkIr$iCKPpT_>azm>$SH+(YmOf=0D!*c&dJz>FN1|CW^J&KN)^dr*hcn zm8CUZ{}tTEB{0H=i?LEW8d8jvvLCh&%i)DB0nLehuMXF;dMBS1(gD|sGQi*5N)R}n z^AqmpP%XxY@QJl1X04sAkE^X%IzFKq1vpvq>u=H~MWrc6qrN8i1}|Zu*H!>ewPpK+ zWpN3tq^zubGy`=ct+5O%>ZJ7AFWSs@dPB*&b4TG#)h76^Jgr3dVTX5Ctsk&4v} z{j?`ajW*~ynV;-VRU(ZR3LQr{$w2JG+sM)zja70W25hne_{d0Oa18U8txe)7ujm5{ zW|oU;oBFC6*c!@pY)$o*YY;lGnN@U^8QLWlvJI9cR(7by!NdDJ%2NfwRe zYHYD$L@g}1xY~+OGpmi#Cnekl?7;@R{7e^UoNP90@!iIB=WQe!tk;lBz@53;P1CKW z)-M2?_*N`@aoBLYk@5jw_iX)Cz&1U%_6MCs-SBc^KPJz<=l_X^?7jGu>Y?mY0htax*;qq`PFB(g3kGWYom5KK zQPFNfoJX$-BkAF;vF!-^(f6Zf#fb5GAeTk6*zKY^n2$Bl0WKE`E01T~(yKnxeal|%_C@FQ%`NzyCZW_M;irKmI=#8?ra$7Uqs>h-1ivf@ zD)s|^tmtbJyO{MqL2hks6_3l)RchfqPubxp8=4Z33N4ebZam2Od4b@~n2$5kkX7bHP2xjWSI}f@^IJm4Mip)2JA8rcC(E><3(U z=?Rs1gH&DJNBRM#118O5A}{DcFB;FwHUcuZqc;>Z-2625{SIBQa|{!vJs#!_HN!4z zlp#jpAd?iHMmPWlr@D8Ljb+dg*Cyz!j|d!(+?ltYEisQM0Ix1AXe$)82m_FxNFt+( z$0PrTI*Sr9q|HpjA;J`Sj^|(e6*sq z{fv;m+5w#Nbk>u%&*=P1Q^Kq{=D@hIM@Yj064K{zo$)mB0DP1bT1L_+9ibUbQp$OQ z@_f;6pTCCZr$Yz<;vw&ZsaOv3uuI>u@+gVO%r_276b))e zFXEt?JueA9lit+pmz8spY!g^Bf}3=^1o}w2^eHj#qXc)7sCVA!oQGe)o-Bq6bL9qm zbz~0drpVQ-zt6A+`{lbiGZQnMrDv0{C?5GSc`wqzzIXX++7T_CMb@oiO?_kFy_{&j z4{<0e*Ffk^?Yp{Pu+)RH%+%C=*){I}i8~@snaw22? zXI&8;EeF%jgcQ8}jH%Oc4aJ}v775WMB5Z$HKp94SAIe58MdQwnNzf}AuEfdUJ<#h3 zch@y?=L#7|YHSEcO-z5WQ^dojjhqsE@niQ}?euX~E0l<`zFe7_IoUu!ZBzAL0cO3r z?pk%juv2*^KcPtB%gpN!2_Rx#oI>6F;HUAC0L~;UBN`u`7HNq7BLfokc+P%}eXcNz zfKy6Jhk19($%4$DuU#HUD1FSe$&#z&o+G?SkGy1(24?FaFjgHDw$i?P5DmV;q$4$rYn zTeHPJn3(XsEeb1z(Ao9H_%c=*`%jfS(?LXO-P(7mjf8mFOgO7c14~O@I8$05Dy$=3 z78Ez}z-q-OcTO^sI#fu$ja}*AUW9D(-Lhew?Bs51N@S|E2T~+Z-CN;mGsmI?_rZjN zfx>v?K1kzJg_C1Hmi)eH3qvTM!MoA094O87tZ$+Vx4Yn#H({q0<#u96NR(szsEk0P zSv{NoE*x%xrfg_8LW&-?u1sjW8A44Rq%+y*AaA^@Lbh>e}#C69ZMtgEnSUDH0-#AC@2C zI2z?i+LXvaGMsvaJ6fdNj(-I!v~?#m!g?6POe3u4ryOJGCwt90haETgj$UIQ5qH}N zdQ=XgRg`}6e$Rh-#}_Ot>u1ImooRZAT|utmB>Pxf;61icY494+>3@Wp*!B9}aQorc zl6B!Fw@TpR6-eWOX~%kr+n6!}NGg;5d556tt~m@-cV?bkfalF%L7SL>H*LB(v&-g@ zSplHNkac!sssuM5K!jd?vwHUTbTQ*`i*QQ}1{5^;Pg|2S2HV}BE1?>G(b~WWmJA$v)@@}SD-;I?r;4SJL0QXujn5o&fjG)j6R-? z(;n3-yhV1oaE7{h7EEI!HR4`NZcub5kzGI31#n7uV8ogkG2#0gKG#^B3=GhWEcV({ zBv!J$H3K+lvDiP-5qgAk1T7I#(zB{On!#jz2)H^7jJ%D8MEtKh9p)e+DpN~6QsUgf0zZYmQxARzIBmzdwM)us4si-2kTGOTZU*eo;g;2D2kYsPvMD z2YTw}C}wM7a9UozwocVgW<*1_4ymbYfjIU2qj_}j6XxpeYiaT3Z$T#f?||(8vRU{q zP%Hj#P^$`4j;pnd3NKGcOQ10&g@l8&op&)0wRX$gET8=e`3di^e3-I4X;Tx!BWIRFv4UGPAo%u znD1e~Xg|Cgs(tRONM(#sW`tSyzXB{ZZ~t+o|{s z|0>U$26Qv+-ujZYOEJe>)SE=w9%b>=F-}Ba#0v$}4T{M9;k10qF0EMW>Jqv6lRT2i z^I2d{uUt)t3U4@&ERG)F%JT{9ID*93Hw3r*sq@0Mp7^xBq-%|Y^I7L`H)@nA3nVpXz@f}ym_95HDe&YoGwpw4B=#c$fkP7wCNrw9P! zvfJCiNu^X}o%mjG)a^e(H&MB&IY3@rKZ%m|0{vGc?S~J>1NmY#Xt2MFq}=}w;Qn#h z`M&@yTUpL-UI@wiY{elHVl9M2wb=g`Mn+1-k&cLP*EgC9w7&c@NLyUvgep8;T)C!zJ|b1Ig@-EdFE>PG^OYZemZ zAir*0rKu^pRS(tLud%UwPdA~ncvp2`z! zOJlzoliK~x%#af*7c(Gl%kM?7o4O#Y0(%wCDEZGitSRAgib@tyzeL`|%d*%7dBgG~=x zY1Wu)QYjv>9z)71VT3|2%rn(+2CgdI$e8jj$Y^X4w{%H4tXEI2Ga@jnaR(X0p9|aC z2YKW)_DZOGkzAT3U4sL>hCsTFkhfW@GetX*Q|qL;5^ordVp4W_BR!T5(*$KYCTE2X z#D--R+|q2KAy9k?%J^itNF3~AuDw3bP3x3M8F$!R!c(FFStR2i>`XVPe?e>(W8-TR z1PEyFZ#nJXO4j6!Yyc+zO4d~ViLS&I=I~7goIE6HzqzBrCK*KTIFJewHPwhY(F%fA z8{NcK$8$qobSR_%-`USNSQMya(7lksSdpk~#WmGD$CZck^>j`<(-A=*pBEURkslB+ zwx#j?_~348WOZeG1Sx1=FZ3j+1lo2Cqh=o>gy3DM78^FvZP#@*5z7JBi)&p(}UWuaA1kZU}qC6Y-l$QGuBdO zVp&wEmOkA%tIDKM&4BvJ=bc22I{H?!-fVd|i|v&bNAD_W43+g8lw#duP_2Lda?jnF z$H+x}n@SQ0ls;6D^a}H)@W?%YhU5?ZCNPI)7*R(nYA3tTf~12?D~NU!hwtEHMi|_a zd1xoeq3-TUR7JoFFHIQF74o$amyZ_(vPCF7F)jfIS0wCcPXY*^_xlrfibT^T z6r(Rvs!{gKO7#?;U`~gx#I0_Sa^Qdxg;VQZEd@hsc|}@i7~;qwdqPF255Ae~;8|gM zmty-AuccQ#g5Pck=5~W`5KvMZg>B;VkKk*h0`o)SiwjZzmJ9z*it^v83PneNDZtSY zVDcAY{|VS^6&Fc_BfHYSLJqvCQz-(|&Fe5T`WrAOdg^|GNtUP7V<`|{RN{)DxN%%S1X?Cdfx&yA86nmy%4hi+TE&Vy*d!%qk=v(~&lN2%xy;Fa9=lWR+vca;a>9vu@2a z3B&8+N_lOP&f!hNXAtq3Lamrym3tqMpR+pg3utw=<9W({JuGZ}fMgo2%8ycV5i)$A zR7^A&Xrk-U9TeruQ0lf!`|z& z-8=Vdmn1xJU6cGZ`&+#^&rx?Icmep{+{z%I+zEz~;SuIvtXW&xvP${|Mz6mGyT0S+z}Us(K)Dpy7URSE55+L@pj9~@R}7D9=(vG^j4fRQ9%%tChu`}b~#zlAd~=7ew=q9^NG>7#b!61*m0YEohV4R@~lV|c1KW&9l>$rCi$ zEC_{nBRR~U`!Y~Z;KUw3jAnH6nIx;K#HvVHne9QAb#I~q?G*ky2r-|9TZV#SV4K?= zxNW9b>b-8zs_KppZtE5;sTGzEpjrQXs3iQeGxRpZKY(t#j`;*%cKB!%=$I+cy$mxB zm$wIS##PkxBWOW9x#rdK0*Gd}x;Zb#FK7Ao&zX0chYeu{i)v{3u0sZ(8uT6*2cJkL zmq{j9DNhvb6n5NsQ0E?<{ZC5LjeC-%u6UFwb83<)@r>jy3T@hmm}#0Aaw=B2wMs7t z!<~Wu4Z1jwXHngkQXtq9PZ;~IwC66D5F3wj$vzoZx-~@-rke5zUiL0iW?Zw+13`Z$ zRplM*FJQK)ULUc20aNd9fywdjWQ_lk?)?WYrDO(#&^}5c>zG~2n=ZA*YSk?Ut%hWT zks<>ko-FXzw3g^@M*UJC~~jggfQe~eCa9_plV*4RDG0MTeR#t)l7P|L#S zAHECRz*+7jem|C!<6ikfb6I2X7?9C{3to zy<}DFz?dkf#^FBAW1Uj%ank7xNbA~NLrqX_4k@cRKs&M8_eORo^)Ni5(tT2jhNIdX z0<(H+;|sftAVh1o{}CI_fB};Ue-Q1}lV z*4wxfIOdIg-GBQEjveMnhK2e8f$fp63`@F|9V(E?~Ax9yfx@WsdL+<8Fv&vBs4%Nx(#E-I!O~1P)JbE!B z2~)ijojxUU-33bWKZI(F`Ouysexq?tFg;5hXM?tl>%cN?m=!>`3nmP2J?=Tr85iQL z(P@rp&6mtEpFd}3?2}SsG@%rG)4!9^<0JFi;Vikjk(#d@`|+7pd&j61v^pKC8LIr# zPw_X|09#z>xV6KF@qVSS}yQ2nKnhxs5;*lT&MSRsv1}2w4}5wE_)>`GbvVxW4_y+60hHxq;mKe7154 zf)0%XUJtWwXb7%p=j%;kulv}YYuI>JCb2Mukr{5-7|%o4$JebU!BA@TwXz)QWogD2 z(PwhCQL23xc)sYcbl9EbbOqyI%GD7R84gdq%G#Ox`>4XIvA=pB75jd$NRV!=eEBI> zU%ihL{tXj-qp@MojUAeaN&ocizj)=4Gsf=E7s4+7woLdtXQBT$;Qfm1q6vt_E2FrrTp9yO-iM-ZY{^dlQ= z_!W%U9TcLJjAerDhPF6O-(A{to1`DvC(2c!6B#ZpaUMgK&&Mux7W*?t#_+~U^0N0sxbDNzJ$GmithZx-mcIU>k2w7&&07>7k` zTC9ZGPIwoi=Eu9H-KQ^@#* z1hxoUd_E&8kj?NFS7Wp zEyrw3Qb*ldvkNS+c#SP^{2U6GNKe{HH+Io&nD&+0axFT}OWn~&!WccR zTK&aBWU68}TPoN#4u)R_s>J$L$N0uBFkrDHBOIIWIayz49?v0|Kg#M##P4 z3gzwy@TW!^*G$tEzl|47cA=6=E#242bhkxwsLf=j-i4VXRvUNNq4 zHjK!jNhV5U${Au?WYhR|t3^ZF=yA-~3u4jQ+;fNNd>}ezE#M{K8TJd&MLo4P8!gL7 zBNFo_0jh+MQ6(CBkCwn8IElhrHoxZI7XN?LZKTXY)N|op}qvu=4AfgJk z3doz+vRHa-=XJcZ&2NG4E`-c8kTqS>O>x(*LwD*V9r85$n<#c305UqKQ>fI2YNJg+ z=Ov528}^g#?V6P;EbMX3D@Ev;gs(VPuB?DCC1~&sm>&K!{5WuXU=HD*5>GUZn-I>; z(?36;!`+aDlYqJ(fJwkKk$#3J97+iVfi4j3IDqc7V|fOZ>LS|9N2vq-xIG8Lfn*}w zK}_h;OU*_1_Hzu|63v2%_$0f5$L@%2cn!Smk<5v&gCslek@8X*j&Xcc0p`XfdP#`l zcTbp_j&s)BKd$Gl5ItFee z^*f%MS-E9y!;b^?m*JXgS5!XP!1jiYyooayJ^SrM~MV`QGz1R`k!MsGrZS#}|`%pG~*3DmK-$VW}fP7aP zZpms)`XF0-(;77h>ZCUz2!i&TWSMYs6wom-O-_f`v}QDc#6+u4goV}GCm|kkWuPvK zY+V=woY&uxP*&xq3bpE{(1PVFkA5eX0o%4Yb~?zHCP;xPcfDqbe@z_CKHJF<5r|El z7{{J)M?+}?KtvdHk*_{pbhWitZn56zj}~D-VQ0iBW}x0-_bQKT z3f>qzGilZjVn9XgB(uVt&~C`qNz={b;b?hy{R6@I^y(GJ+m-pJnW-+dyo{fP8U0RS z(DixXMo7S`BilkLprfIurl!#B*K%DOB38He$Jtx7(dW0?I!+9DIWS<|_`bER*=YZA zKI5*D^)_%UHH#njPt7i_aF4F{4;~%7p6Sx}__5Ca`phM}#D#~TH`glX;7q2OU(1daJxG~8Jw-iF4Y%X`-DQsOVT44fWIQRc zg>tzMKnSgei39C7#DQ^rmN+X(MuBQ$Nr!|G=)EDx+@C}kKC**^PmN}=fq*&WDlWQ{ zd=YSiW!l|KmB#j-&(D{84oqd~m@Sy+GDXPU(TR-bGWIlrW7%|M zb+yu6tPjMYHQ$q&OT38Xp#cK@qk9N*<3H!)mMVOmPwTRUv{tE#>%N z>-)8&{UG_d{f$T~5jDZn-a$wYKB<5C*I64F%ajuPQJ5`;gqGR3H9=yq2UN@675S!M zhn;?6tdq6qn^~os+XTAIQ#(JMi~y@qo@BW{F2OdU5aRhp)chz9rBmVdSqq@kg520b zT_GsL^r7%F?KAfx;Q^7M_~ zfzSZ$zu>IZnh)uX4o$68H70SErfl#D?j)FxE^H+jvS9|-R2Z5%mfqAOwHdB$$7Ok{ z*P_6v{tsjC6kTb%ZH=%*RSz69@u~KRQorAJV8RzBU;jY~%E2%wf>wUyc^_K|_ z68tNZNR#R{&#bD*xTzp*rG7Z(SDhD;GdB0~D76b)oL#=IiM*+5` za0AUjc#4PAs_>RxZiS;_f|GqG+;fyt0$qP134j5OJ-FoV{3NzFyZh%TzMRBzF;D2p zfLNyo`+fd|&p9a`2MGk`b9?U>cKRHmuWlAX# zY+0xXsEB#Qxr)(@3-30uWrqqv{QxYynUg#TTx3T+$eyU~qFJ~`2z4`}lg?dx!V`oR zK+x^2pj18K4lOw?mYq2@bFE>sc#ECNqjM+l zR}D@*Lp<&jnW*=F^KTb<^KR)^eKS9W3i^L9GPUa$XrIiFoM#we;~^co!K~9oKW$VC z=dV&pT#I>B*4^XBKzP&)`Q`p1V~5q_nF|K?)hVC4r_<@JLjiRu*DG-ct_tZ zVNqm8voNohIj5E_z5SnAJf^y@N2$@nb9YU)sK|gO(!7)2m(Bou> zQ0%~l(BWmk$S7EklxVTjmD1|!hSS3nf9ql~3m&ayvJ|%B3X0`kLCc)F`Gsi48KkyV z73{vCvg&)JzG_V;uSdV$xyd}N7_DY-r7taevXslk-sNucp_bGT%7!LxYMW{?Nn3*J zu2!^C8-JGlTofd#yB0ReRzNrfmq+jv)Ja`c$X48UPQqZQba_=J!+ItNrm*VLE2Ct$ zyf2JbS!^~Rr{IyM!plO>l*Scoa^8)I{xPed+1L148A#kF@`yD~o2jC!Sg`hB5`-gR zZc25Zx`Y&UB0py?wdk*JwfH<@N^ZVt>u57CZ{6U{NM=jE6$V;25{xg6k-o~r^8>~> z!Iiawx+cQ6qR_Ny`ReC0we0-|NrtQ!h;`ZvKUiGVi0Rn_JlusXKS5^ffVoH3^!n0z zKx9%!fsD9+JHYZvuoD2fpKx9XkjQg=vN_-8U)fw*45->e*-uEADgFDd^3JB&<*YW| z${EnuRaH{ZVOLvK($E8g!W~yl#DJuln6)MAHRA`nM-}(9w<<3(YF?;~E$1=Se=jlQ z#Y&dmQG}zopN@TB2vFSTH||`*oXsyqI@FG_P+JK11;i*1VX_RhGW}-iJxSaWjR}js z^)wds3v@tp7XAYJ%i(TCL}Mljz{lvg)6r@7iM=CGw7&*-F(L($%(~~FV9m+clJ}D? z^#3^*z$t-VZ+Q8O*U98IoA~@sfk_t08!T6J3CQCCYcBL+($Ox~1)LIXp{PVzJo5>Q z2t6YMh|?>s>V!g3$rYAkt9vG`3rpjQGK0K_d-<VbXtL}RH_n%r|o-ncOux~L6aNesx z3{?RL%l&?7{cbqBEpjiFFna4TL$snT{RnPz+q1eSGgrkBr~40-K7rk0zf{O=7AH;8 z70e)1J4a-AQ%lVD8ob#$u=E;$Oqmd$DeX2BA94qZBW4G^by?}ig2AF8f zEeHE7QxotW7-wSRLHP=tEsC54Qs&LeBJm|0ZsrPu1mZU;qya3wSHoo|tU4^xGnrdTUa1ecvviKxI9uTW%1d0V28+aEt9D$@-Z74q8>-f9 zgWlw4$g1Y=r+@us19ft?Y#`I`%m0g+(neRr*3jsBJgJtO>n9Qtjl@Kg}?zS$kgmPE<2v%k7+KR{R1U%KeWh<{-q^08=NS`FM{d-l{0H{et1I5nV zGU#f2{IF`xJ}7VW_d2t5l{uXN-`B!Jr+j7SPBp@u;|x~o4yo#laro`0E7jB`?+P7AEJ48{Zsbr6BDLD+H98KETtiZQH|- zEcvR(-&CNKaImZ}l=0rb99(X%Wy>2DTeA;W${twnN*%bQ8|=u}N2XkX>S zCH**Igsqhri^z?pejJF&U+GF%fPCas(yRmL*)I=m7RWRZ_FW;4Q6qt(sR+LD;t&y5_1{lud<}USy&! zOXIxmTn!X`J?ax1cX)BI2T;R z)m`P0=BK*hR0@caS6J>ZDaBd!)5eHx>f+IlZTdTE+}YkoJP4#*uBU6*)wtl1K1Nf? zd9OFVWvr4%-Cx1okH-DI*v}211C2-RPT*=UUsYpJ&8LWSRnwsJa=9S67eCXjtdX1E zu0j;g1X3~V`eg+R#%oF}Gfx5jOxd>J$`%HQcs+exeq20axuny=HWG*3Bw8}b$Yn_T zP#5qB^vtUKVhC>OQU0=@yL_I5<}`rN^q|oF^AKE(yL=K2mLty;Q==}}N_9trLD(*{$;-o$ z0Z%xMo{<1F<_k7)iiz4Y=DIDgB|ArhQSoep9998iOY$XxfJZg>N~-(I zA+0m|ek$*loK%qak!AF;75C|jlw{HABvW^$R-ZlB#BmvgtTj6I*b~jhO{;5auU->zBR@n4QRx^r|GlnGD)+38Q>LnVk zyMP=cCbN#13?q6C|BG_ z*Ar~}i{5F%m{~niBfmSCym+yb?ZxeN>{2-W+C!jQ`9V#QPolT;!g9!@&Rf{ zDXpYKzzGr3(ZIsO3P>!34!x~IW)}IfEHQjBzgOYRSSrkdEL5pq1uL0VjZoPn%U+Qy z?r83$ji%1A%<}N4U-Gv-w|$OtJ09NNwzscjQD;G(o!j9$ z3DHAzPP)!glWu$F@mZZl zbX!yuQ6w?h1%=&bO$48^!+;U9ZGuQuY>o%r8=Li=;9{RJWp7eV7BTTuYj3t2k9l}1 zI=smS>Iu-CptPGlomE>>C@s5|95qAmbV>4=CZKi`EjRvVVmW1(qU~)h`o&MDum>^S z4{YqNU=ZS0GVj{5CK9nl*Y9AgptsJcT)J5w&A(|Ck#uoTDU9z|eY%@Vud?%mQ^#5$ zB}{N*F`B!6$ZhsJ&an?wD(e-TBce60q0;WfuO%dBD+Mri=)A=EW@=aipysN8MYR6X8WXd8^s;5_A z$abdES+$GTS+N%#`aDx^2xcx>Ll72<$qj~mj97IEWjD9?00%cS5UtM28;}Ab?XBSX zu=Tf9Cs8qf&j*fRM!ML84WzjdilRVd7hRR8je*kB8G zcYkRp)2eq|6s=R74x?GCllU}~ShV0K?t;v`0xQcV7^}6G*_cde2A?&?!4{Ij+oMSd z)5Tckj>{oSJwr$e^zS&0RjiMRbNIhv*TxTaV+raA`pQWa_=hr*f|};}*vGo=)=wy;7oW~t1^maf z>E%I3etIzcus}dP@oh*F1x(Dc_#9tDyidAgWrEGJJ-R8w3WT+tu#CIWqs@KSAoovD zEh!**LprYj7Y%tTeDq_=wCSdZ#g+t$8AUf7-~h$DF&4x|Xlb{&nGx~HC7VRz)BE7l8Y+%`w1%f{=j6FA4=UAo_9u{ zyL0VpR3zV9V)1}yMHT5ZhTMTf91T+SYO`dFS*4qrs>C#obOqzVt;YWl;2v!V^1$$QY(40HNv5Yjz|qLCH}t!07a1dqigPvk{B zDJO#hrTm4>SEQ^n>SQ>apv3~pD#)!qV`Io4)nJ*!imFJo)nC9wA^RSV)@3bhzr$}K zo-2m)sP`2>G_$ZJSTNItbVz2Gv0OPMu4QS5=+G0^X$k$}b9n~b(||qVmOe%aJLLR6 z4>f0Hyi*EmlYsFGV`O0`=+^`%u*TavYY+EhX}czzNB3nfWinKq?IAl@$Slf+sq(h^ z>h+N_Z^9>$^`~9qAUfX!{=uehO>P5@@5Rebxc`Jr|7m{We_)fSp|k0~LDRS5qO-Co z`se2XD}}7QHB}d)MT*i;@hO=SVv}Hsc9D!FQmq!MIs7RRU%a%eT>6+hMoqsv0DO3M zjB}|YZ%#>5Z^?0~HFezl*W+T7_x!Tq)2EYTW+4UrBhlC8#mC3n-Q~ye`IpZfmOuUk z#Ln<85C|u-AjV#&lnPOV3L3cs1%f3S7JsvfiRNEQ1ixLwNbu|OJ|=YDp+G-X2R9&> z){;cjtHVVfT6cjOd`JT*e3e@TAR%1yfiMsEU;x46ejO10FiP}2hZ*1(?lz30>NY1#;_C%iwUVLi6vC{2S|5Jq1hvhy9izZ$k@_CE0O(bd+_(twE#FMHB@GbL|Sl*SDboPZ<#) zc}48bw`JQyc}1L}JwgG{++$;q()W9w%;>AuMUy7c_9@ww=8lQPYKYOBp4`S4a;7rP zp$EZKwUq-sAj>{?@KUMDvCS+SCu;WBQ$ZOL?D*LaX#9+51f``nUEJLE}RBq(3E2y2-en|A@kVPLIMcOR-EC!XbhGw5PE92@^Cdf zYH&41nVhGtS)r8dFglOgP$h;u+g>CHJnrsr<|^+|lzLJ`XPBlE{pdZ_yVRAlH+Fhm z0a$Q$dvtJi`*Z0$ig4=2U7;=-75Ny}s@d~5MErYsG1f2XbbN7|a89iX2d^W& z{bI?jvyU{q_7g!Boo&|3h^fyA)pnjp7xE#f$_cF{xVC*62ExGkwLq(u3#_*(U za8{Q5R>Vx!!g;;m88PASLmV?diNweYhDUa+$FX%VTi{zl&&ZyoFUF$c(2HENY&y#~ zmy67KsKT-CF$uKGCQloE(*!BEog(verod7W)ylN3{dU`^p8bHTcD?{0o27potLgr( zAM8q{vZO?2FR!woAx%D#%F{&-om;fzPJvH;-hh8II!#HA@?(Rtf_mi}5wOoljgK0V zu+L6ley|S#N!HsC@uwg%KBT^HPJkx=i*{*kd;}Du8$U`?(*@#d#YZ_JzctLhm3_4Ua5BLC+%TM6`Bt%9Nzd zPb_;V0_wGERk7!-IqJHpO-8|0yM33=-mY3aZ&~ObE-Ymg=j>wSKZ)kNz z>aDxtqW$8 zjh-BA>%iJ$PEOa6nvM9+3ZlM{HJov(McHrJn2+Gt7J2~;b;w@{N7bZ^2)Yg^8RKS+ z+-8k?=+pwmAB(Mv-Mqxlg5aC2XS&CMxS-usrTx$)@WYf=Q7P+Fsg!=_<=!|cW^!%Y z^hWxv_q=OT>wnw|?|p=B|Kc)u*LU3vLg>J0>ROE;REKWt{v0_|67eO@`8AF?M+0|z zLjZ?k&2gAk#F=3G1t4jP6u3z&PO1@9HOq6C{!J?i?AZ{sMcX!b(oXaD4E$j+`qL5X zAyZ!ze6YRTTWy2W^#;NA z$I;Y;{@3Rdgg@1V^$b(NF7tQX7^ST^VW!_cyJDiY&4xlBQXL3O6iEt`so2)chFY1+q<^{LNx6Xspg@@2sWLGeodd$m7bkOysI+)*Cx+qC}p$eYS zq{6nn2Cbj%bc6A};A!ml-N6>s8sMgE+Zo>85X9f(E zT!%WmNi7oLzr|x9O(^P6o%ETgoSHXIhu#L)}{5XhS6_ERW?kjPCv4_hgECh z!P?DXAX9|?yztG`?^VSfme3;e3IEi(L!`~HB7R_zB)S7LIw^4YVYpE!?TNlm8uNF_ z9pf6<@rfT$lsg3RJ#bHaB(E?Ep&^f#A7@D76U^9;kX|Aw&<`V8KdPT6gD(WPD;_N{ zEVzr+=-&9{b`sJ(sza1*v!_!Xu|o9Y%iHn&4s4!s7b5k9Nuzj8jq;d!sg|gYjOyI9 zFyRjvQ3=HNGJ1qtSBP8AO)=CZ>16){LA+f$QtfSWC1CRP7?T>;h%+g2IE%A~Y6pK; zSevd)#XDH-Ju(wBWzMYD^Ie|$Gc^7e?pIEvJ}QgR)7-oo+Wr9IjsK7=>>L|MEZyEK zQI}^NWPU`4b4PgO5acA8_ewbl!9qT9)OjIBr~JN{|5iNnWv#zgA}w)BGi>SG525th zRtM1IkUS*f(S;fXImqS0W+8qETR22ZB$QfU2|tt|}^@oi87Y^Gg^|1SyzGI^qwePhsQ(Yl$D>M94%;#Tk}) zqcRAt&xEscDXFGyO6mpGF8h5N)PPbYjCO~8z2z$x+t;?1Vso(6C=T>b zYImyG9~(yQ8N2L3L$w3(W+Rj{`xgCl)mmz`zYSCgtxmPXw8zdszM);6uAeGI(PzFE zuU`i#+!R(?p2y?7TF_#3$HruF#Kbv?{1|rLo*oj+%k!9uFLhd>o!&J4uMAO3q4u(r zb4;1H*7%flYHytmELTM+9b@{KHjSjMu5``Ws55`sc$O=34%?3;9Id4hi`WE?q@>1c zZ>kAx5fE9Lb`~Nsxs2w53`-;4`S~}$5hI+AfIkV;MxWKfzLnH0-CkrV&MJvBjb#)~ z#kLlsfyO3DROOhC48seKSE{XTm1fnX$GA}=5&;_MrF3M`ofUByZ~hCXTC91pxy2!G3p907PsB zXYb6YePVu8KF%=sk5B7+)o25aLCk(|SQ9{ zMB9`)!1XWa+Oj>}CszGHx!Lqd7QEL6UZ$I6c_VV~ZZ1UEbaPZ|92}Rbq&0a*NGE`W z9U_BBg8z0wYG}*DQ7AcADIg3{$bVDl+S(s(FE+=Kmui@v%eBK8bTOd}lAuVU#6`$h z-3W#l=^Yz5lu#s`Xadnt;i`?4FBne&a+)}JJ)9^;i?#sjhgJfaq{oAj8r>{$fHLdQ zOxV2RI8vMGCj4(0?QmVW;BFz_TR=uPx=^1Y_dX_kLcZ(i??&66Lg~~~ddc>qE@v<{ z)$ABj^z_*TcPpGQ zu;RC7MH+>Hb=ESJ7yL&S9t#c5u8^UI`~h`HG{t$b4t6vq5kt|?NdT-eX6o)P@efKB zlNBXViV$Ede5&6GeFjLAxXr#`E)cez;%{y~w)wWWa61it>|J=>QJ$TluAOpA9-!ap zPN#?f!H({8xcLd@CFPDoCirO-k2{F`oC~Epg8Z(f(QlEl&-)83M_4{cR)pCVv7QHm zk#V`XkuVc{JW1j>^X^di!y74?PT zZy0?z0_QZO2WWR?HStJ(L_{_5UMly8{KDKt@rwFHNHy_q-NHlfA)=kazImAh&q#A* zA<=Kpir>d4H0E6f(Oc-(5G&tJafudG=2IHd9kjcwn)u`Q5U}qXC7A+GNOMFXIuB^b z574jk>LbZL@p?+ zxyTy)XDUr^2*HkcM2n&`QMJs@QMMDoabnAJnmQ2Z14?BcP638-77?~p!8ua@mBSNZ zY9L16fwvBi?cIY(AEMrBhS7=HT4_gdHnkf=F*w!7gp{I8@I2iYa=&LaO=#3adv;Vv zywhM2owPMIJ&ey(+V2?_MQiJqs@sFPL)kJJpAX8#-?D|U4rS$12U~~BYOtd#>~iHe zn_5?2Z00kru82k{L?6Tausi}h-W8m}f&{DhVre>*Vgq7@5D#J{=FYY)Fyp}M?&?Nx>UGa>og@d!<6!JHx|)Z4nN7UZ;0jC4UkJ(JtTXR1JhJ(`YfM*9r3a7 z@)i6>GDqwD{Tr^P+#Rl_VundOtTK=b9~+jjhL9pYy(K5`3d#nLujJSAd~V%I(XdpA zWa zHkm(*19NNY?3HSh$patLcq{M}`huV-M@Ng{Y!#d&rwImp-En@kvyHLxZ7ceu75gE{s5R4tVefwbe3K!>H*ly-UqOAb6H1|>9!jkd;ULq=@7 z$EX6P)^Y_|wDch3^`Z3iZ$iX;Xks1HR8p?778f^<5-qfICCo+eRqIgjWu{CjJ1MP4 zu0P}WEA75HZ$A`$LVSsWOjbD;WNCktX2FPmqn^AQCe*{MBvD0Kyk|?jB3TKFb)<#v z{Te0KqHaqGoyRpgd?=Wfa_$4AJozPZh}VG0cfM*`7T3#q(H9tcxsq>4!7**n@@MNS z*9p8h6!izDeT$f>bb*t*hzP?E)B`qD1FBj!)Z5}85V(tH8&Ec739Mp|th~|UtjYFz z{n{D}_P|jfAJ4sP#-pQ16zbIRFJooKD=lc91-r!bRNvZ@raDL~ig;A|3_km0cHW<) zw`}b31i$#qO(Hf`b>?sIw=hoH5adjq11u8fSMaN`sez@+qQw*x_SHT?QoI~PyRbQ^ zl?BIZP;R-oLZLeOyhC!!!8h?p$P?FL@ytd?#Zvx0Uzt;5X(j{6sq8sD>{OR3hZI(q zN0*JKG?I_aOOfwGQ|{5rtqA`dHDr}5f?*$BAoE2le@Y8+ih!JIML58!r%L@bVdOMB zh>Gr;R&KK0?&*wV^Zy&f%%-Dm{)!9RHk{#QypHzN4Uj}oP0(2955aqgI1m0K5>DI) zF0wrXx!iK$Hl|nLQm$l0P_$}UFJ5XVTP0XHKquN7QQV$}^d{uVCuYmD3nCJpPwg3D)=hxcj1Jrl-_ zTqv#!dx;NP3FNK^mELrlA+-N}49^>NJCbhdu7 zEno6-E2}&$6zxPwt@%je)s-!~h6ptDa*xW1@S|R~u-(qk>CmdY(N~0@V=00~u{arW zfC_Tg9x}m#E#X=?9FjMR#8CA2qNp$FuPnB3d$zmY=@8@y6_i)5;sh&Vzxe4* zY|&;2(a^%_n}78NR2|N6alW~|y#6Eb{U=h!|KWgD9c}*2`=+Ssx}m6|ZgOOp&5f?a z`KO@gmzLVDnSqHGm8B>NSLRo&xGmtof&JRWE7=`~19yqtIcZ6BA5RT~wMRnFj( zVWTv)lUB7=%g_XocC^~mZqOtO5o_ZrnNH`f!FZOiGqUxMDTFh}u%`;Qiu(Neyn>@u z7f)#M9Z`r+Vag!RF4%bs$tCugj3EDnKeM?NqxNk1FE1mF3)8YmQuHb#&&8QC$4c+0Z1J5G2}ss$DZgk;bgr7vOt!k z;S>HGz=$@sJ>y-HLOHExPF4P5uUQv_b!mY->2P(@+=45>c__-PIk9&}hKE*lBC~6; zX<23Zs?!W9;iCRB-t>kS53msvF|8Wxb2()voyR%wc0HqKY)J!CGL|nSTeVqZi@hwO zUw*Ej=Mp#%;dmK1?8SbSE@L{zSrN%lo^}Z?!8!wXE4TsrP3Q|ay8bqis|CaGei9_$hjqL#TJ@_|gFK(yxy%_4bEfpk0}qzzR`luDld zF;(lY0`|#4u#bOrHNejrP&DfE0Qv53M`;3*WS9-*X|t;i=l;63k&=T7^yYz#k^s>JK-(^-JpCc#e7Pe%xe zt6CZ}?i==|csm<1KyEEt5H@72&qYlzT{7Wezf zdlx^Ta7L38rU$59RZJgLIVAFVQWW)Gni4qqhrz)HzWkncNf*~Obd1n|3ZgDhOUMT|No?a4BtQ5e9!Z!*jbzYr2pq)ii)A#8YAL|UfI`f@QWalG$qcwD4S*U z9Gw;#d7|rD$yhLnyX?h;tAWj$-}Wk$^~p&wfRgm7tHbR&XWwo20j!-dVNmYY+16m@ z8X64;{5EB&NHM ze{789VpdfHOqw}`^vEiFFsp*qnBkI6f28?C_%<>vtUYl2NaqMA3RGWeRuhf7`JGPg zvD1SB6wF-X2Ktt#fA>x$`~rK?w-w`d`IbC-@*WD;MQN<=`#Hmf1^hP@_vej zY6bPxkf|?x8bMSNT0v=Q5%#Aud&QIZd3nkmGH{5rqazNTNb+oSmH~zmPiq^ zib#l%=dr~ShHKrp;({wqAHJdvT$^(+{VBQi=7--m!0xqyPttV98tA2s1~OA;gcs-! zBka5YPV`iXYNSiH4L)h--8hSqzRsVAycO(BsiZsuLFwLuVxyT$EP^cC$U|NrK`nKx zt~$(oq1VlgKHg~UosAL8I(jXkApPo+1^l?^A{=+>bC(?AYc|Y4Wo7CX5bt$VGAT0I zf~^te&BRJguo&BulZTwW4`o=TlO;07w5V-`1Tgma7M$nx}F zciOOjV-0k&1G2TyXAUCqG79zmYV-Sqo(e1I9iO#_yo4@q%UQ%&JW&Xe9FnjV-+MTK zpqm3M5N@HRG{Ul?%P<+6y6VtPgrXe=9>FxCG36H!X^1(Q66X4(hq}!^?7Q>Fj@~cN z^l)ab>~P%saqW8`b{7O6W2gy3D0qwTaR}e@h>+38+KRKb6x#TT=O(}Kh?|CVwIl8| zH;dAD8yJ-v%j(eCGDG1y6X-R#`pX3c#a!W`yKr(=05#1#m7gfd!!0d(hPcCd)w@0( zGfiF3v|5${J+=(0q~hQ35+?s{Yx1A^m5rLI6cH--MSJP_Vs1(Um(oOX(^3vc7K@%E zAw=;fHa16Yd9uLq5fksh+T_u`(2?cJwoPlcMUX+lL#Tg7=Q5iOb`FMn4?NP;{CXFq zqe}6-tg35t@>1(Zq^H4q-j9#}BFho5a^v9qc2>myhqL;3v-~GV|6iJ=kdc$KqoJ|0 zg5kG{v*WkqfwQBX&A-glR9R7eoe|Mj{LGKjg;!7&IkXYOJs)+ZJm1x73^8u`U{1s7 z+ekB2Exvrrc|xkA$`_tCpI@&!7)~L7P%s9T{d5(xp@vXTOH;>q{|izPX)tFn<(EOP z8y32w*itS?(Fk2jk&slc0(8TKWuM(K{7}dA2Z1>i$v#B~B>OenkN`J#8!R{HXZ{c# zj8Jlq>hhzvw4je)CHd!`3h)HqTjmC2-+G@Oh~{+0f9)^>{WWomg zl0I*2X{Za#eq=+3FVEV6maRqwEh(b2r)#gMVYxLpofEZb22VVfp!8dm>1NAdB5 zIV*7eylyT`&3|JjAI1o(~(YgPy_yya~dDj zXh5peX7%k;))k#U@a@(Ag%AK=_Xk(>qBo(t2Rz#{C>BkMBNpTDrGqZP&DX*k=|zD; zpPEWG)Zx@~nWfG{d396j!1kOFv-~z5$85niKiJhdY|7@qzCaMl=f}S!he(WPXS%swtsdG_)vjvs2pqeUDB>odLBS|ivB zl*)0B*Q*oNuTxqU8by}Ul#Nh_YgdTL%y@8fyS`EgR!p4r2=PaS>Z?*IWBS;%n2o`! zw{TB%px$w_+s5T@)h~rtwB?v{dn%$Gzc6CKdvO>8{&KTAr1Fc}Z~C2D$v?L;9))kb zh*GUd$RjH$TCsG6&;yhz*7Q2%k#dZZGhq`UL1F}JAE54=CoOMebjLnkH(t+XKsVj9 z=WN_Tt`!KI_&TmUlTUe@mk+@0>N6+a{sL<`W>B1-2z4_pzJj57?OL&fUydD}^^MaW zV;;r~N{W9skc|!V>#j&mP~cInb0O&0Ao&r@gq23f&aADuV$-3DP{grAGkYs8uJhjn z5-DN-Fr#DMVcjLui6^h}Iq_n!6f2!Nq)pC{?ElHiI6#ubath@-Y4m@2dXC>ek*K)M zlw4SK#Kg9BvdlM^G=3=8ddX%Uyq6pu0SyX?BXY?GKc(=d3zQFH$=;E_Q4L9J7=JD*eh3jbU}~~!|=hY!)L|kAa@lOYq%W%&}I6t z+|bC}lmtnCwHigQ^dKb*u8CbwPOht&+b>&xc+6ilWh#WBw6^*u-Q#jfF`>{$(nW4y zRlmiKl{j<-SJ;L~4+LEr`j;h9JvoFhxIx%9zr~!NU%jQIFu3~1_)z6Pflc`Ct#qs* zKOEx;maWEe8ojAtw~HjqmswG*Gj+Y1yI5>>_LHw;HvV<9Hv5&Ud}*~MxIyCI%hrMN z^!n7#L`};wU63h^d`9?+>YBYI?3N=_x=v)dH)kT;Q2RzfElw9kk6lCWdOOnxLd8Hp z9#-RxUW*maz2@Pp+)K|Mk<%$xDm$P2_veSk%3qlkH0wwN+K?`G?RZ1IGyc;+&Zs&( zhSfR44cZUkH6If(s-u`1RsY5#ZTXNK4^$X-RXQuqGJd)vE7`2U@jfNWGvo`66m{ZC`%o+-$IrMbg=x_0ahzu9wiHE=`ZQyT@ z6O;g@%PeA@iiQZ5`ecm5!#_s$**i@l&G*Pk^kJtlcQV3fyc>`p)@%b5o0o12~p-n=U!M`?C3~kvogF z5fZB6M;Od_qIn*qLe_r$LHLq;YsoJNjyn_-KJYUec;?~$WVqxyXS>ioS>pQeYzs+` z8;5tJu;OMth#MVU`{upg?|~UH=ndN6@BB;%>_O{qiU^P&L#bYKHC%pYck|D3hp6oY zBA_xCDhe|-VbCx7m0$yUh16TLH$h)>0Im}fkfn#m+>EHDv^3B1jnGOtIQ?t>qE~%p zAOrr^Hr)DVeP7O2U-r_xZZp&p9c-M)H{(H;!q}5mlb>-m?60TruO7 z-|Z3vccU6XUFUc6%n<0qxis~mgVB{h1j8A=ypin3-#P#g>^#Wc3dtz7XN7lPkKe!A z5NWe0V}0KfvJlw+2`&Fg|NfVu<$t5)zc8{&-PUfM9q|*N@N4(%Fetk%F^Q-@o>f!+aK-O?@ zK-e}-lQ|`4IwQxP9FiJwA^c`^kt`s5!Tb@l7Lk~{rE<~A6ggxAcE9bgsBfbk>!Btr z>HHY7d*Lsld$Lpb>2mPk$)^n*sjvlm29ZfzC1G^HgmI)baq@~f6AEp*o4>gVH4pqg zu~~!Y0_8+LF;qLGqXdY>BROMwiT}to&_dpd5UDU5vP7s;<(aD9kYUl zUS1+6=zDv~eITFU@FD(v<^$YY?y=ZmZ*rAA`$Ii&fyhigX@$w_NzdXoQ4EeHtNweJn&=BlO?$w+Z*NfA>ryG z2~C>zR?c*AHel6w)+#S+1F5k!&|z)snc)fY3pM*Fxe$Ki0}@V$Cy&>DTY z5Ws-Hweh0J5QwC?EdeZM(uH-7tkl=LYoR+ZrExQ zeMcuKDbdQ>-+a8 z4GE0|q|!9ws5|6d6SB$}^bC*5f!EtCDHxF5?(UjM%zW4r4$;lhP6&%8OJ{e#bv$V8 z%7#ys*Vbb7QKQ#owdI3X%h^Jq#Ea>zn^v{l-?zL||vX#~Ko8 zE^WhIRFR0VEq=sgF7dup$j)Lu_rM;foY}eF3&-zI)OQYL|+D5XmeovCcmMDnS5F+e|&&L{#71J&VRo} zCU2?j#9h>6lOg|9!p){xLQQLvNECPd12YS^IjSx_3VE37P1n*hL(`enMi$b z7i+IhxzaDRrz~w8F-r=3vwO?#e6+(cKe=5+Jo!EQCZ2)!QxJ3sct62-93Kv4SDkY< ztFe>Z+D5-y@}N%BR~3u<1m2lP!N)3-g)Qk&V+FMv5dGQ_rE9V%uZS%WGkTv24I3*^ zK<)R$`@l+hF*|Hs67(yc0;}*!9%~3|{;dUvx2)nS^Jwztkh>O{^lMlBaG7}UsVt~; z`^Ncz3lqGz+UU+hRelM^uU!a3QP5%krL;yUA(&DAM;U9wf zKSqWhU4YZSM-Kl&hf5wSyLE~9zvKd6!owIOjx=FS&yE}eimoh8%+!&_)31zPe+VEb zYDpQ3Bw+DfJ61oQ%ge6F+DDIsrA*BnIEX??Ys=g8bX9eA3+^*{eFw7Z_su^M-Xl!L z;GUo1!Ed&qT{{n~J6z3fj;Yd%pKT{rGbo^W*&p*`d!{}x;gPQx!T9e}f82k)BVIm3 zzK0G#2p9G((;D!@7b6+m?c#yQgkvjXJ_d$~bu@rDr*rZv8}K{C-z@v*iLZpl_2!Lm z-t3tUuGhuV>2gL`NZH#Y+2-kO06RbfSY4BkaTBtj(+~K@<1D(}&YAD;@43>6@cHh$ zAV&6knX=LH)75g|0^Wcw$Y4B@8WVog9~dit`54?_3nn)xV{uxZX~NUmLpe93i3>jY zo|O4@9yEzWCEa=WC5`>k3rP{FlmsE(5Mpz&>#}Z9GjuL%@Q2kC%K3I*-w)pG(Ak$G zKYhGCIQhD>GxcgCq+Puoe3@Fabu#rCU2lVrC^9v@C3sj4{1Nj{m|KbQ6 zDyiZ*z9mNZrp*HarO@>sQwLzq1pS)nboxU965)_3jn_sUJHivK_GZuc3xaJQjGzHy z7mwfb0|Fyr??;XD-k<0v71G_}&?i6I;N4dmGor+g@ii%%OnEl%dauqWo&N%%$UXfR zIV6yg43KcPhl2q(pmi|9da$2`kj~o8IRr)vf7bycl&)`AsD=rMYL9oTe5C9O{NJl7I#<_?6wSA0- z_oN{0Zbbmnh(D^6H(NA1>Lf;ppe#$bA{N0E3}qxOM>meyR`4|esPKf*L${)-yYHNNkismHwXClAe>$Y z&&PBMyOtBetaKXeC?bj&(e4SIX%C8 z~)9Mnov`mv|jxj~IH?%R&*CyG7DJsujSghu4huloDg&L$%VB28gZ{IkdVhF@*MGz0lBuM-Uy6&!hQKq_`x{NPQwMd67MW|AYJ{D*|B3~q;Z7TL;fE)K3 zj^y(tM97kJo_vaAq~5YPW2!b_k5Eq0PLHt#-mH;z&Ja@Y^8^=ydF29nb+}FHEnrPv zzYrr&+0#t|biBU+9jn$ip1MMLgJi53ZSLNP=+SDbP2-03Lo^XO98ZYvj|~J&>&VS} zKCGg-Nh2@>_pYGqks;MIrm38`8?~tP3Nfm<^6o*^a~tAO7t7d&b19#PL&BElg>Y{) zt;TqR43=+Bgawv~kExWi@C-*?z8=E|EBo<_wFdOcbvjxbJ*l0)DWUklsXT+fk$xT- zW+<|o)dUQLFpObZfWmVq)P)v+pa zgy&EjX(i(*TNSRj=F(s$M7np9gHN!dv({1NK$jRGu0g{f(`H47#|Bvn+R-e>~=pOQLRF`d(cnV)>u~Q24bu6N69ETU^uRpZ9yE* zK(UD!m=h9P`LD386c@n@T41^3#8DviNLAo@&|lB7_T4e|KvJ3|6OTwyyhsU0KXE!M zu2zCp^y%0WW{H3o6*Z^?G;Wb$#2PyzzDa;aQ z%Q`V9bU_0BUgu8lj8F_W)x74tQ>M9lE=HfhTPqh$8wWBI4o#9-_RceMDr0w^!8X2^ zE6R|`dVCYcE5PNtC(78gEzRoar!j54_~_E9U@D~`YU&cr7sUhh&SX-afa9`qWk`r1 zn}KjgCCtB6&|$jOyH6s3;8){Zat`EC_?EDl1y(z~R!s->>yn|vh6a?%9!N9gs|5S3 zlOH|p6!CVzg-vUwwp`jz&K5^zB-WGIYA%$&q77Ku{9ZeGbgd-55Km+^=bO60ZeZ1z zKsGSO8AoSirwA5L!c11{qIkr5l@z98XpqWqgAOSs$tZ0r?-sz9m{k*5T4ZpL+Rj~Y z0Dq%+j=t`l!W1LR(yFn!Cv;dB;*zlrx!&Qph#4)IKwT6rFzF7>`Y18|4nFz&^1aj7 za%hPDC17bc_18f|yGUm(N=lvWrwVkifoJE%5C-a*y1Zwly!4Teg$+2?X*??twTY7# zOOp+)1(UgeATkB6g;G@RfHO8kyV)n4EvTIrN$X{>O1LXGO*9PC4~E? zP|Z*hm6J=q2Fg41+nrrU&J2koSFOR#WaX~BMg^%kX?lzh3%(#r+t;1!o4B$P>qh>- z0Mue!$NNhcj5e@cLZnKMs5m5Qu**WAeseD*LJ-}k&)W}^A-8j(GY)~c(77dXl z0#@oyxFo?yOhQE>C_FM@|w{@ud#-w7!)GTxU9%N(elw zle`NXL8nw!_^aG%S#-kuock8x@4T}#1XH$JxYkU#LU|Ly)x+7a*_w~lu#^hgLrJA_ z`&Ek4T2cr>5jblyh9a6zB~$=&*9mcO>$GL53$8I_Z9O>cJv7h?f7USl`7Pq=r(`oA z3TJ_wu}YyXmQjpr5E8umr6!h_B)?n=VI#=Wm7t#NX$2) zO_ev|`s9}3mo=%b(7bdnK!OQ@0TXHRX>MwyP>WY&sC|QINPT;n_`?SmM#Xvkh5hNk z!ko#(X%rRV2}slgk8blU=;CZ3Z?Y`W7%sM=I>}t1v)$vUXXOta^4Q~|&Fh_OqDQ_% zo@xg#l5XQMXSI#|ErVWC$u(osfh)Y8HrxefR9b5^P(QTVuL+7QuhFbol5UKn55-tw z;#dIVNb7$K_^E)6StM9v$trR-J&TP96syZTq~o%H2x;ySYSkpQlLrx~whY6)Ii7XB zl+3^(*yv753UsXdRj+u9pKQT9U_wk6LdxXM?Mik6$QuVdEe@1PE;%WM@s2b+436->GoQr{!ICU9 zNTM6<%YqbqV7+~qgcGr1Q>v9K=do;PX?$x|?JOL1SDDxpJo&Zq|m9Mbf?hg zp+lsaPB6On?Eo^X4d)r>n|CeddPBc^&E2chkXIOdb0|!;XuX=KvCL+B zK~3`|iwef|Psw2BGn+a{02rg3wAApQ`=dhFDEOHG@bARA;%rkrjgtcnqe@Em@izI| zW}1i|`6?9e7_n#Az)Mn6+(uC;3^$U+LUC{D!>4czb}m=yo{o}aqe~I7i%IJ7qGaMp zQOJu328U%+NSOU0BoM%-*ea&IoBUj`XMG3 zREb5g_F5}kLE zkETr91y#ll`O{+4cXzk^-Gv+LQv$k`^*=vQ|@A{4=iU#?>}+aWVrHS_R5 zl44x(YE7Kw#ZmtJ9zMRa(L@@OUm2X{)bp%V?R%w)n^utHBszH8mm00?M$_fllO$E= zrI)*|2o0~V_KkK;FBHLHTk}GUPZ1g9o?-|-q_;MN8bQSW;5_G5MEs>L72r!sd6pFS zapl&gI|r{npf3$sKgc9~4P3{5lP5<_Pl1K{Hrv~X{Jb+ThROM8&QPcAhI(&pkZ?A}8Sc6pbbPZ}lXG)vbf^tG)d_R93UXh(1!r{5 zlDoUoL+9Q)IpdW!w$YhPAtn0)kGW(`<0VSaQ#aRpIqL7Yv-&(iGXv>*>Q?EHU+uX9 zcT2DYIeQseg}>okxw1{0r`8IY5$MlK?*`}F#H7fz%0!R=o=4~5Li(wK!K|!u;Hz!0 zVlRNqQws#Un|7m_F$7jVc=uoM4tEhehSBjy+<>ZU_|Esp7zWsHr=rxuR1^dUsvD>T zw}k7QA2Zuef7hyISRwjT2EH*aq86H00lp=}yZy%8!?u%6TYzm!A#6}5P7=omIJN=4H#hblx0p5!;9HJf*(44htGNpFQvV%Ir zMpji?!MiA{)+-6DA5-oYolE0?)P~R?S0HhDZCCUH>UDC}7!&+z5x<63I`y%2nZDJ6 zsrtZ?-khqOLc?y3cw&ljzun-X?l!1_HPq)9Dg~2W4Ab2`IaTJCb^#Fe8U~B<#!~*7 z4m1f9bW~7-e0}~a>U@(!Ql=6a007J2e;Nt?Bbxk=M}n$9{mB2DD)O^ilR)_7SL*7= z39llPg_)#Wq_ikgBB14h;)Gc)U!&5>099jcvU9_QQ+>U9Ug8h!jUG{LM8IIaKbrh0 zDlBx62?2MOxn$6iX4E(5vSOQ-yLFY@2Q5|`;PK|zm8niWaK;_#O|(D+3)9wj=TY#M z3LMsn54O2V_;eL|Ku>|fb2eoUqcK(<_pMl@N6vJ?n2)cdgkT+n!tf+SnGB54{;D1( zmL(#%>oYlkMsv?p6QYl|$BfU~@gB1mQfKC1a2I)Dqt-&2x0o8mo)tPj$(z-vRAZA) zUC+&A|5(sqed}~}+i{i`{OYwrdeNbb|M;sRQE*)qCYp1^h@R-vl&64{AsxN#Y~E5` zFhi93H+HN*#=c0%f^tLSS|U|B2tpLud<3ZM#|6O2>bdUdIVBlzU(4>xpBWj(*Cdv| zWUz70#e$>g#c}d5)VbS3h^41&brbe=0?S1{kc(#|xGs?{BEa5@D5lTXh|lE0v5jPh zR)nHDz{`k?7Y}>HnBkNj@{^r45=TUrB9sE#S%?LqD7;(e*@DrP(Y+!RQ}j7b-9R2mkQZOA8*Fm!$M6D>1r@%Ncr>*XXA z(=%SDC{{3%Tqs|*huhe^j>YFB_=E}3oMgy50UBkzE2jc;GtejC>xl0BAs?|D`RFP@ zZ^g`6L>#y1H3+lnUhKYkC}R6`@O5x?JmTJoOG+0t(Dpebj&NpoEP-gwLN$`1HsYvH zAax{rTH&V}z5mZOLoceNLkL1aU_x3c5_zMJl>+xpnI zGRJm10`NMP$e^8;1-W^cP!D=2=~X}+`D>@A0q=^F6VVU~5{#33>zq?y*Rlg7+O{WY zdQ#UGZL@N`8pR^W^_uaiX+;}Ida%eGrJlj5?QBdg%Ad^xn@YrkjviY4CMIl$0lr^t zgu3#z^+4(65Hel;#0$fk)RQSsndjIRKP|$vWv@g#O?zS>21^v0V$=dmu4AAIJ?h%> zm2X8iVcHax1%@C$Hu)F#B<4mRyopDk>1_UK&1(ifhzr`(0c-&pC+%qlzKCkFkgWwO zM_N*wBCM))Iba@|Rui}&qT~-Q`0k_)*&^n@ld7ZVYdo~8?S>~#G+i=N`=sy>6#8$~ zO_z7$bxIHV)Sf-IQCqTn&fxp&iqF%Mb_EunvO5~? z!C~U`(q0GWi0wK5mwg5Eqqddihn`V{|DW#Sf68S4!@KzZMGLCZYr7_Z&`Ep?Pdtz- zIzXnPg4!7vFCrS1(^sGvZR{1PuIswF$%>--`8-u6OHc~`wC8jEI+>xl2k6Yr;NMym zEoLnZzCUa3J=hFsEft^{gvpPj+X9Rg;H6uRlQly%gC`<3rUchhJ?d|K5jKAX3+NkL zJK^uZ0_1+o*y&pT{N*&md4}cc4pNA_kj$z>NrNJE7w>DP!ifMyFyAf(rGnWj*uvyp zr5YYEYj$8p26FGC+>BW{3AJ`7iD%~WWEs~r)K<{<(d zVJ0Ef$t02n3GWM&G}26g-8RpOL@hEMOmrv3&lTm++<*K1ptRwyOy6?IeI$~Gf_SB9If#rrY^Bt)F9MbC zmU07zbUTrAFcW#~!aacj6A*Q6PLtNEHf4UgUuiR+SK*_Y>p)aaiX~Ax&^gn{39EoQ z(rb;-zS{g}Q-5CXWfA2k@8TiXFCx0=i$->DHss|ESx3HTKW2UuyG30fP)G2uBRLD(-22rR`+XOg{ZA}uTM2H zk=#!?R4g%*n1L1^nz(2{Ou1)mQJ0pM4Of~jLp^$l3-4<-lGAS(vt`Ui9|A?(a>zCF z7;|pjh*>b@*n2FI1~sLWq?ZEbdK{yiO_Pu}Ee(SvLJZnIaAe6M7l~{xQYKuHMqdp9 z3x`M^@<8?qz_;^A(N0`*x5$9>g z2U0R}hOe@^#EF&Jog0`CPa*J)t5MRHEE1zr3rJv1W2X@xS;V%KjLs3$h1_qmlrT6l zmr{g07w`W#AJ;_zL%A>C&&_d5Q`V;zigc&64IsdQ>2ZiUUoW-m#KzV(Uz8fby0kN8 zk!iq=lU7T3DiZu)-Z;4Of5s9+CuA#pX>5!|AY2O zjhawotpBzIdo>HR9q82-Tt7Yz{l*sE9ZR&CvHZ zqW8zYw9XUCJrTh_dp`Yt-1GmWoBW?ZK-kX4#?bbcjD@Yqe`V;^bbs6cC_dNKEqjm( z766w)zsb1x8f;5M3t)0B;H$hcFp#+GafO0juG^aue0#^)1`1%M6#}k@IgjzUquAKB zYaqqTQZ3^O&|IIMo|;^no88tP&T(Os18sXbd<}>B^@HBs0RvYa5Xf?v;wH`Hz}6IG!=N;Tpv_gL+`>{E0eKd z1zP?boyC#>$x#Gg0ailIQdBN_b?>n4M#Os6h^pO-g*|6r`PHCkGyg@_07Hv^zTkHY z`vvUVgEQ3T7pwQ_W6ZjL0mftRyAU@Rh%yJO!$n~PVhVwbivKUD10y01#RSPwq(G6) z+~ffWsD)Mr+p=JQXx%iKgFa8I)Zz}E{1Q>7?KUnF=X}md*5XLG=>gwz(VE02`~EA9 z;s&DgOe@jK3IXAEMFwkh_o2{W*|bLy262lhJGY)i41OF%MIje5l5A!h1$>dOZx-YT z&%P0nDg<&1=w^A>FiFR~!3Hv9#;GK{cC+++>UaB8NDt^gee+({1%mRKTyd1vrCFC} zBcLELlaidj3S}kNr>T-|K#R={K8HRp%@Jxu>L*H+oT|sf6e@7ydeQ9pbHU&%lk4lz zhbv`ou<_XEB{2s)ge=4WRSXtO=QF56NMOe^Hj^^7!je`zxX~wpRhWWx1el~tNuA;_ zIk&P@O;zQrg{7;{amS_d@@yTJtkrsov{cDzcyG~z#*w~8td`?^4^W^gPrQ%76dc<+ zB_!CJ6!N7uY4>?{k~Bea-`M&@zMKpJdU?(p(V~PS(2ObN4EO za?^7BQV{j3NIG@L3n5uuj(9EhBD*w4hS-fP7UWMptFFIb`=b)aVzc_^D$n;1Kr z{QB>Z;FE--0gJv88^a2$5-!H z?bmdp_A7q~Cla@?6xHD5rlzK%<;fEg5PA7}%Y-CY_aDXEIbjsDxCLl?N10Qd+ zdlj{4{DI7SK0yaZAbfLb1O4Z3LBRNO@r6J0o#bm(X<4kHl{n35kwR)}5LtovoEG2$ zL8`EHp?}UVxen@?2wF7=9&v~kx7cYUK)P+lb6AfK%BB2a=2s?(?sAkS0*_je%$aVP ztZn_OcieQ0q%+9oI94U-pKfi#lHEtNR5HcPN=eH}E(I_Ri&!H$kJ&6lxe-SYwzp#o zD8nqo^CevOrWA>+Zh3;`)Ii^K;pFFC>2$N#kp!M!<<;Zws5}L3Zl^{`lTI32z1FQQ zV`{68g3VSE=rQ&Qv{hpM2N;XJW;Wj7B+BmWwGc~(q2NetmgJ+B2S&skjb!7=Nf3pe zLd2!SGv-)^pDMJFG(57jSi60eE8p*TLGgI2ALIA2vtH8{Rj`CZ9cag$dQ3P0$LVJg zswOJpXlfL>ibK*Q&U;S4~Sc{f16{$h2NMhKJBv6(P=_e5!z0yl<;cf3l zRFLc~qR_xEr}Pu_Y5j9GYh_C}1TfJ+Bk}y(X|){i>k@~qlMPnsyf^7_g%&oksOvaZ z2Q1BQY0fvRxilC2BsX)=v%1q?fn8)T~4*@Q-8q8 zZvgbhoO4AmFx_7sPUq{zkB{NkUyd0giQqaDUtjDf$W5nELYA4NflLEscos+>oBNK# zjsSPS)D6_6Y>Q$2oC+0z@jQ-aNPY*eTQ<;*3<#Mrm_SPu{FZTQAY_pdnfUGZ=wT^9 zvNLXh1)cnYbJQoY6jLuGvGb>%a5y-ZLpTUUJ;6oLVCZ)dITM-B(&@ypQ;cR$wu&M7 zl&H(G&G%t>@694+fUDGXe5w^}_;&(i5MT^?j}pjXJ)=<6Doz0q8dbEH7}7~Ak{F30 zcEf(m1c(qe=}=m!y_&$l$TDawYqCH-d*D|MVE1G2r(s59$92qCi!Xb?b7w}zmcfD_ zYv4Jyy9aN}S4#b{1mp#|i-!}6E4iHv5F(3t~BMH?ba6jWcJCLl^72#shPTj7YPPh)&!P6i$(7K6S&IeVb%0<06~3s1Vc;N>cJjuw zlhV3vJ4^?_TnMt9ja(U!C5cE-3Z%ID+}w)7*NRwyJ%u7z#(N4s4RnwIqWD$on)B7PYpZrQuhM0siE^kuJWrY@or7i$S9UW*L6ySqF^~D)u^kt z!Qh|}c5T|ifNG*dj#`E$hr8*L>72$q`?e~X;c>-8J(FsrUiG^+&^%vgIigpm_q1ShiKec zG_`|qxS8knHgm}NF6W`7WfX<-zzJ@bN)0czJWWxob~bv>{x4jCkyPK%3r(tZHHnn6 z4Mn@_@V?j;8DR$RUw9z)?4?Hz=F|Y}vesjUUZj2W!pQxl=|mw0+aPB8h<#S+1p(R2 zEdu-N51?-sbwGDUc_HN8_4#?VQb+ADDVdyA4sI)VcU}R2Nu}5Pmjz2z5xeG-k{xy` z?LYcFwp)hA8tj#219f$KjGH7hIp4v>dlwS==Wskc5Q($bw_Ap=ICsZ&L=guqMt4G2 zNNPdd$v-k0=R2$Xkz%4Hk*XF_2?c0=57dUHUrm5q=!nrRE1QuwE&WYB5wW?!o4Yo& z9K|YVJ6qW+^8Us3wM{llJa+y0YPf`=6{^kM7f>E%n|*SRCYS4VSC(c& zx4F2pp5R6;%oldTU$eH&+Y13D;5D3V_kA0Rb}NLURwLh5{1@gBEq;DhbRROha}{Kc zycc89At|BRP#JD;*_j)IbG}wyBP+uAypP8pbf=z6CzNUB&{BTWCDA?Z3oty9mvHs= z#EI$Dqj;xwNH6e5-vZr>&FgO2AE+Yy2bnwy==_6WZ9e#XU#^|QpLL$WicspD9L~8@ z-BX#_c#oo{&1w6yw#cLyaZlhSWS=OizJEGIH}8tG4!i#eDA08J za=-bRdexx)*H1pQ&HR50D4_liu;kw#`+w_@v8|le+3wo?K=aNAREVTq*WlHM$3$y4 z)hewHtqari$P#iXB!pz+sPNSm7QR1S(E%WDNLMAzxY&!~!)SoseGO|bcs=Tdvuoz< z8^Z>?<7)Elx<8+GR(E$5!M|REqtG*9)vCb1bwlcZU5aJ;pcnNn$ckV%8`9f$BjCXs z@tNHCi_?sLKIy6lrsW#lX<=J`F zCSuPXdBm$v7v+WCKYu@deh*BbW#ftb!V|4I;3qE%0wd2hp$n*(%?+sJ<2KnB+Ik<_ zUr!>Ew;U%S!f;)qF$&)+Bbq5<6i=2s!4lTf+6qu`1XjV?V%mR>=Ed5Ar{{(+vcCHg zGMmBZ#of||r4t=;ylm|}C?}p`)_-g74C&`H43^Se_cu7j40lM-$UnqSM|1=*mQo?z zTPm;pXoJWQO)7p5^?5+Xy+?~dev{Me$Dbn(Xg{FFqZN+Phz0yx8Du}AP-28-k4ZnY zq#4|(OOS)WoYraP9Ld$0SD$Bl9k{NK!mzuw*_K^4a2r=7uDuoDHxE63#7m~%MVD&= zNNB)hC%OE}=-D7<3@-vcc%KGLLx3#;q=Lsz1VC){h&LuSnE@k_fLB-?AR@rA^d3MW zk6S*aqg1>Gz|o%`T}K;5B#Dr&cSDBakOE9b;NR8)O9IXtfLNwo+~6z9HOMSrt^TdE zJi2!Tfv-+ei4K^3qM0wt z_w8@@WC0{$cT9YCJno4rXiQWs<|kyo2N-OWX*@2XcB8&rruuwN+eLTz4t2$oWy zFM4{K?}Mqu9r!l{gu5vMfGK5M(gyh-oG|T%pC^4f8xKoUzFU;qugta89z%v$z*gJU zFDrj41~3DJH-dC*Bu#pbAj$qu3FebZ2OQ6U53&Rmz-bZ~pDQq^ir?UDIz;E7v)FSZ z0%Ic?#?_{xz!&vs-Zq#emVbm`NsK!INuk9j^B95A3)&&gq$pkDXUkz#R59FpF-XaY z*!EHj04}P*5`1l`g*;b~O&;?xd7|80ArhQmM~Gdv9Y6ob!n_3smDzEZ31a5Sv6Um0 zgNEjh+HFXin;6!^j~0cWk%K~LaxFr?(d?Xmm=)^+z|5Q+$jg@iR??7SWn!xo+URbUC3Py%uW$=K%BIKB}_LH6;K3JNjxAipUhH9Ctg;| zliR0RH8VtC)pi8Haa$DY{3hkin-OaiG!~(%S~sIWdi@0%2SJg@Acd<8rc~0?)i2n(qMYtyF=;J!g zS`xCw+?XC6`9d8;m}3XckFZD=FT67(+Zi8%l{5fkQx`q^Wdj}Z&k7vPpcxalSLs%rTr z$lO$b#Mot)J;puSM*Hmg?5|SX!P*evPm;r17cg!*Cj6yz>nfj|;wkI%0X@pc@IMNb zY0(kq=a8x)G zjf%>>wSP>k5cLz6&ZkJwP!Faw^NP`oqjOjF=nC4|Ec+Z!!AveDRAd3wY8xe))1BK7 zN*3freQkFJ&+1RkB-}6~hr~L;%}h#d{aI4Nv0Apa1M`(o@^KQBXMIZ(!z6XLZI@KW z#n$&a2bD!z717fN>>zbrNKECPt`o(vBRf~N(OmIE+CoXYDIAL-sr4`pYOy04O`n-k z*Z1_JO|oND(tQxT%4*GUIj=^IKMP0D=&)6e6VK<-FKQ;gJ`y-x7uV=nYtKOqLFX@* znFigJl6v>%7!UNqo@ut1%?DXo8%K4}6!`zt-t8iJbl^L2wWU@p;x0k;njAX~Y_B(n zdKap(p0Qk)u20xZQr*BtNb>mFTE>qwdHF+0$hJ+qP;P;%lrai#nQ`BS{jLb{zJKIW7N6)J~lh zSFXod{msycn^4+;f7l3)p&54t#0p$2h@pV(D3<_(Bi&3@UpE{>yX*Wz@+Jh$7iB)= ztOO>(H@(e#vYmAm$`Qc!TBN;&X}s#_Ln<~;J@-G-wX%1q-|ErO-Ew?@U)3R@$#GnOtyl&9+M~q^GjJjHN?O<8NxG zROZ6?da7GDdiuG9`yC=7Ne_4W%7v2By&)3+tZ?VIWaJ+CBafmeMzDI$`X%VPU~(L;b~;SRb?k&#SM#UbabM);`uJ zVWXbqr{E}W@>Fi48a(1+#*-;_*o{~4L_0VciY|o7bEJLh^zvY^H0OysZJ94!JSx-` z6+0*l^r@>nOZT#!xD-q@rY4^+M3)WHDFlo*`t__UckcLLX#e>67}7g9cyjrwIo?s2 zewPNF6bK6wA3bc0DT5=~w3=Owc1y`tJ#IT5?E0GUS4xI_SwL;Ad?LgOFq(O{j+bsY zSw)xB9u4In1rve!NFKtaD-XED=l3xcDBJ__Z_E^FrFuHJCFfO$`~|H69m!>rxWq~< z`y%M_ySRhYI~%c1oVvM&cBf2jSsAYKK$#%7 z$oC%QE&)KFXFT)HT|pXnHm{L2h}N28&~L`0j~BMROZoGsG~Yxi z+~!A^*yn`FCPL-ynCQxsjJ3eq>^J8dP)=rDt~pj@n42arcUXk`G~9jpS}4r5cM~y;4gHb zBR!8T(Pb<(85j=htfv42nxiuoC74Y?Gj-6bFnkorM#0ulxhIKouSt%GFSQ@| zW8>{D1Zv*``B-76pgoupgjG`D^ zax^@oj4N|RM0)MhSe1o|&pVN1pcaPY#&u_E@kU7e=9CqySAQWhwU*VEf&+l+ES8v+ zcxO~H2rBIx`6ROXHoeYS0bdpC9~ld6+Lf4m@WoV)mvFm6wROPMF)NqEwG7Jx$4F`I zM2T(GwD1=bdR1cF$%Zi$Emj=nrsQi+n9N1{6PQL7skBsP;G!=}H&+jlUsuy1LL*G zJAUf8$2aR-a(6@_rW*RVABLr5d51-kqC4>qunIJkX2x@)(s8D#rdB}co~O0-`E=*(eF=^t zIws;^)y^-GQ~){Q*|0k@pop<~{;5k9@xWfx7OH(a4m}-7+s@lyiZGMKBmJif za>1v>qiy~DUz$PUco-1PAB~EU(0~20|NkL4WG$SWENspGpW)*FaPlt7y-Gj1Wj~y} zt0Pnz0`NREY5z_zgUj!NKf;*N;T^S$Yw=X0sWAQBZiN&q4S(PqE;gS(w$@#kaGnex zlndF#5|jSy&(F{IC*8zO5Vt8ps4Th;^>tPAovldmdTKdh@BlSncX)NL@(q1T;xQ3BmUK!#%Qls%o z*OWW#z`Ogmk}=Yo1)Szq2)#x2A+h{jENwU4Rmx55)@I?o3EQ}#1Ni*l2a~pG1m~u% zAeUd@xwS7RP{F_n?@L9Ln*dXgJaPnrZuE;>pOy`&W|U3^D7jUgwPu(?897Tl&Tp}K zi1T7gNrPPr%UK9?wf?W`@otJ!#ffenc+k%A%%e2~qX{dArE}7LLiegLK7BPY7Zk%F zLZCukCzx&O9J7f;n?G~#i^oUH;{Q+^P;@k-deiRlC-)p9&_Dc!jIVN1pw&iBP_O*= z-&a*94J9?Q7x5bW*vogReWl3oIys54gwx1D5c>xHO6Ye!Aw@aKk7&NDlR5H>g^MS|>!}zgy*o793ZYIBpUTP!UmYpaL@Q z{<_Rt54xPniP#fayc7p6{2HO#pW)Pg-o*ZBo3ne7WMJAluD=zJ$$LJwt8e}E7h4-= zHzq+W`|-YARTp+Wd;W5@c*_2L3j4(wtCz3K>z=o_uM1?iUhVAR^Tqo8I65$yzp=k~ zhxQ={FafieZ8&A^l^8@wpeC6ll^RjFnFENj^syBmx^?zuI8P8}ZPG}J!*3;8{~pQ! z$vqRDcuSN=55l~BUW0q>K;XzF9`IH=!vO}Vy@XC)0_t(;C*tnIehzHCV+XA+K(g=+ zE1eVvCY(dg?sfp%tcCS#Gf5(%f}+M+h$cF9o@0|_aIERy$-!ijqg%84!!WOmLSl3t zkiZJ?=tP>eq&4^$3K)3e=RD{ZVP~Bnd_c&`2GijZ%9hXSU3BO(pI|I99VXwf))7Y8 zE3)5lv`SBvEx=g|@yg(D_WpUb0zTnAs0BQ| zkhSRa`cYl&7~eNXAA>L0u0vm!^yDru)O=Za(E+eT5y*_xh*GP-<8tKO<^K$$-XjAF{oI`d_5o1CwQq zwkGPdZQHi3O53(=RaV-zZQGfZwr$%sZhrghbE7*t`oxat-!NmXHP)EpecoJK4)jBp zY(IwajxD}E95kUp)CsSRSL%X!)46cSx5Xo6%wTX0Y%TH;^chZ&%?92aGjn@MKGp!} zJ@M-bsd2eBI!rz0y%WReZ)0k zmEkdAjaJUkG8-(q1w(RP_SkClF;)?2^w&z6(UlVY}l&Z`>g+Pb402VF95;I!2?Tb#OTg#?J#i#jTtHZeDb zT)Af+5-Sqq*Ez0($T@S7pV!Sy8vdN4pfowzA>kV9KX228DhCgfVG1w@ZMGvk^ss$Z ztW;>!_%_{khcA^ok$1Dp_L+KM_oBA2j^biewc8#$6bnC3rRzpn8#PG4wx73V$z0^_ z&@$I)Y|6S0UWDJkU9e?wizssIt)-n_oh<2FK3`0E^JlFK0%(~Y4J=m8mxAod7D~A{ zfm@?Pq=cCir1Tax&_VE)%Yaasu+o8bx72ly+1HtoC$rDlGIvBRgiofgz`>~Mzy`$+ zc&Yi?_j<_~FOvOk_Y`yCO-E1}w6F8QGEusfR_T_FIZ;zNRVfl@TolOkOoMS09vV{T z&<%HtlW{nV^VdZ1=SV8zsN_gYQ^0PM9bY=Uc`$#ZsbrdU44+a1h zY-LxS3>MQVyJkckBAP9=5@An1Y%J$Qfq@|a-9UF>-OxpW@uoQCk^sXB{^H!1AbgV< zd&?M)`Yq`{uk+eI(Or-~U`kjz1{XMoQ~Cu`CH`k1;Q)??d2p{TH9gJ!Ffv3j9FX}< z{#&}91~Hs1|ASDa6zFZVeD>FTe$b95x^dsUa`{m58`|9a?ij~-y(6|5pmhcDy3XN( z66fe9i#%-70-IG%h`bRK#=#5!=F+v|E+6gptNa*`JHz!_=_R1zm$~e6`C;(WF9AU^ zA*-|vV)k{+@4}^p;@jeSn}trDS1so*Q*>P3S5tP>?*ZJmoc529NErM{SuPOB^Brg{ z8{?!opOp${IXlvJw3am(^`eamjulr)d2fCp<|{B}qwVSseBFTTT7)+UdDbMq z1Y_7DI5d?(toKL+g$Lk@ZmN!R7K0a~HSQ-i??HP&Gz=;WLF6*j^Vov%062ukj6AYQ zd{36c%BzFmR#dO+c!R7b_z+#^471;e<#Z{qFEP^H>`{V20*D2qgkiqt!9PxceNy;# zTE@hD^uCIOC6TAqr)f`X8<@+no>uAqGPGEvhB`-w=+;gr_-qTjt`gR0AFKvD7Tl8{ zI%P*L+!mxb+v1omue&MiA>ONGZw@IjZno4XKYPLDZPA>iO2{u13gxh(pNMhrntHB)FV?fK=QL zsZwRx$WLx2$Ib_KbrHHq;!ClWtL_bx_}si2^i5XHX|VZwegfe;WHOP^8tc_@)_G*i zn-RA0kZ|%P245yaTYhoe)iav>h0sKi^|X`YgPpa>hqcDH-jalGig*YFFX^c7LZ|}s zp-ckRsY!f1b>z{ki)tFn)2+Q}QVQlRgS!YbNgI|Og4k$1XzPKu|A{)E3uh{oc;%aV z#Rn1GA`rTmR)rvrD8D(+PNEJ2Qi><+0TF;B{0E}~PXHkz1X@=++)Z33ffLvgqlWEj zJ1JbGBw}v{^L%uO4?vc5ffrx35n)#AfqfC>W(}5tA2%wPfXn~^J*#)gi868qrmT$| zAH0@*UxgqG@C5bH=n^^WD8~v55@SXe6^#>xL)tHfJlANRd!!(+h=vs*^qyHRC0HSO zZ?IfQalR%)C<{eq%Gq)@e=1L{J?ab(=S&H}9_22ftIKAjkU9#fyScCOGtSjq3MQ-1 z*8;}p``IPYm#$yhzJNC-{xleUNs>UeVl_lSK+Fr<>>TKBh{))cXVMWcna%mF+fK27 zx-WP-kP)ONy?!7=tvSe>_vGO^T_pAZfbps`#(;amXgA>Z$;(ODz2=$oe#cIg$8BQl?`-AB7+Ph${L z^ix_Rtkkty)rQ{a|J?v_+jF6*Vc!e3TMpcUXKjYMV~m%o<>-;j#$sTOcZfh?L8@oK z2(AoE%;n&p6)euyBfb9BU}}dYo!MG6ezj-l#(Hu9n)%kEhB(!^=U=G~-=kGJE{GMn z2-=W-nG}+w2UAsm5j0>nP#`7UORcbj7b-> znO=|s{DxiO$HFAfP)el?4yBNw2c-kd1+nznewEkmSwKx51y$sSIe5n^sd^y|iHDx- zny172z2e!@HM3af|2Vv|Z(fU*b=|ZwPB*-fc$x}T-GsiAk%~MYAyGPGYUbM3lM-a^ z7$H$oU&lBx8EcwR$WTAtH^G=GxhYjiy$Cb7w^>qhAi@;A+^FW2;z@?~c{!G^v+jBv zKH}do3V8iuOwjRtYvNad_ONEe*G9>#VWMXuO4BgkNTtJd>xgy?I*};%A^sXC8ll&P zX?+h$yEfDN;gA}Z;G&l+j<-)WeFx5pM0>c_i@x!F$+qmF3MC(9Azq*deScNpNiif~ ztaBVcU$w@H;pvyu@M5*(zH`G-`vPneC4vAM&VA2%H67a_HNyHrqrnBlV5Vw0G0eqP z*dCN}ZW-m++nP7aOfwwVQRRJm-`nxMRYD4Azg3Y?d2$Hk=q06htM)>(?=mDUYy{*r zq>e+3U(PmdelC5fJOi1^(q>d5-rFF|$H8-uX+%kkJ^|)1@?#!+5Zotr?ErRhf@Wvu za^@S7hKcW3JSF3_!UB>;ZGz_h;&^jmb59L1pEHZdH43k3%}{+7A_)Z2BQ3TGMMs3d zBjBh=Ce2ROmy_*_MjPsDDMw`gNYtEP?$OPyKr|hm_5*-lwC|b@LR?rtqSa8pR>t15 z_Ov|lMHok1%vjWJ!QWsX=;dCfT7LrU^69CTw|;w;U!0}i&U9IiOi*_=0eGyXQV_W3 zqj?s3&dhj9>rk<}x!-zSG?*?4QvBBRb~|G+X#%wMD@_-vGICd%C4HLRiIcM;w+*(i z-J|f?^5U^oMAms&E5>UgERTE?w0`N?Q+Kz0DYKAKldScrC%&ipCA!{;lnpNO#=mHq z1MDB%eRhv}pYeC#$X)}@yZo*+r;xbb9mw&bi<{%5L&8S_&j;eN-xP(W&P@arCA7+1hjas=`ul{dRwhN@CDOV9J9IKjb%Ih& z&}n^H(cFUJTs;zewU+tkYX+TWsK1`c;2q-9g(sR#&BM#%5^QrZ-^7~V0-G|8IVJdv z)kx_Y9OV~^Mf(&~Ie@YY(KimoBfn9|u*_ByQ*h=~5R&0`4^LG}`Zkz>dL=oN%QY`1 z!R%*uj)t!9j89jQGlF+W3eWLzc%n_ zomKFsS*x8tUtD^c$jJmSDLrhq9ARa@GP{MD>#n`9nlRM#8+AQOH_X?<=(t~(xFQaw z$6enj1UBz#(6y>=ENYuSO@<6_t?C~@4DoVR8>SZz3SW1)>7_vvX_qYCa&s-v z+i1U`Gz`DRJj~|iN6c~T%pB(+Z_G2l>r}o4w21W8j?vYoXX=qo1nd{zqc{?YrNN=suWTQiv(3j(v+lQ?-Z(T>K4=6l)`H1kyikKJQ+i8o*kM@fbQ z59n&|y?&wp5?O$#zg*|P{}mO<$pex~`-_!L$Py(&NeF#CYvERKz?@+|9Pt7FZGKgpNQ9ucX`YH%%T64hy7co%}DG7@o)1@JE8o{;_od z3w3)0nl97Yh`SpVA(dSipA-G1&SGC#6!nFX2|ko#l@F7vg~X&7;eO3|TYAg{)W-*4 zwjujOz35zZ85gLRV-&y~-@v`gvO290NLui!MT?5uXnVyKg#;l{yl=dqGIivronx6_pp9+-*7 z=E?>cfDDA^z2=4w2uRk>O7BG|o-+C(xz8x0;?+x_^M#Ue&Tv_Y377u&ji~%Jny4fds3p+70T| z@GC1vH8{1SK4zi=QMbs+#PnExd~SUM|Fb`A0#_N_`N`8STmMhH@&6>lDgUp}_1q5<51Wvpb>(OeYW!5JsHN72mWylj zPJ+qCp7iar86Rn*%LL~QK;y!{Y00~(xX#U4)(fdUJeq*g&=&mj&eezK{q*x4zU1YR zIr12%9rFIx3`OoSTmsI`0Si<*g2OV8u524R=T|tmFS54(RMN%-rT39y9@xi%Cp;_? zhpW=?B%2`5ASKNTvPfO9vcmuZR58&(&-a~2zM#m?a0Ceo%^hcDU^X^4To8{RJsfei zdGH<#Dw20P8mOPeG0X2GP4eUH7R@Tn3672M!wKlB(z`3K<(ALcOH8iIM7;c1YS8#^ zc2p#E9uJ`8TqVAY+zLW1K^nhFl3`BVgn4Sp#!%CeT6)3k`zyLr=E?P#AsU?rOns<=eTPC6 zYbrr-U?9lhK{LTvmR>r>BNYazf05w8xFR^O&Vg7peJIwN(%l?^0v9FdPD`+ZZzX#P z>Pj&dAi?z<#q!d|-2L7wXK-P@A%xK;Cp<%2Am_1kMA1YBz(J+RL^I2!H*%Oqy;1xu zModj%Yf&N>7`N&eM})-ZMwZJol&c*jp^?clH^}84lnfskYS=1M2at#z96okm z=Ay^Oqv7Z@hWqW_+=e2-pH}-%^H&b2QZjl9@R^E;ns@$OpgnI-Idd=H?8e2PfCla8 z(BY!o<{1=3YNvgKMHH?8=#jjQiYOO%N1HgYQ~XH?aCDEH8w?x=GTesI_oR?H?F2VD zJETx@mo{l_1Dh6c$F&6eHY?@^FpA_i;Bq?auOHKF^t@ z{|GFck!2NP*Mya%DQT6FD~pk2u>nXyh0UkxE!k#8%zy>&_4)Vn*y;Ie7Y*3zBcEx( z(Zg1Mjq|N%u94_%XMWqRW|(`1b^@)vz*_X-tDm2L6V5GHE0y*ETRNhAg?;nZ29tA< z$Qp3~t$MK(!u$6&Zj|ebwdwdwHaH*JC~62nvzZIdt==C{cBx-0{tY7xXi}Zus6naC ztLE@7JUb}al7v}qSgnb5%2iEVc6;VJX-*KU_y{&9mOHZZs@;9uf#;$U<#DK)wB^;0 zPupFysV6SW9n%FCiS;p%zM)#!Xw8Pw_bQXgm8h^6VkU}vzmHH?3bJ-${#f_&!(3PF z<(p%&?zIO6WTdjW7stYK7E|c0aR;#)J#C}O{JEmSx24?_b1zgGU!n?#1=KNEEyT@2 zuOU)LS}wWKly}V9li;tmoTR+cY^xw*uI8VI)EAl|h%j%qIQUzoK~(HsIJvtAbf)%r zZc+0sQ8lVRtq>T3#B7LL}!UEcM;%MRJ0gC$A%>tMIO zrv47e4U%YKo=n94WR8VDWqq^JxT{FucH2Tv?z%qSNe`>$v2_ zG)0Y8Gu!1b)8jf-8&JBCUHM$W*g62#IGz1*ha7>3{(UGtjXmj#CoW8E9V}lC_vq@8 z$@G^elhNUm@6gaGVZ%ZCl2vwXVv4+_y(YA#el}juImUVh2n4FjZeI?3+5hkL)+KS& zEt?kA>-Hk7!T<}%t)zu`7HM;fwWJ;D5fnv z`TvjGBq__tt^Y_0$7+$@go0tPrRbXB>5ApWxw(7t%4I20ymc))*Xs?x-yMdRrSE< z;@P&Fxsz1OwhwL7Qrsf}z{VZBh_?|8z5*9zK=4~U7*4`7;n@>Z;Yu0cB=rC`JgksF zLfpzip&mWVCA`Am3FW4EAv1`o2a}We0tV!lIiu7f^hCPS+~ShOO%a4{^2w5iZl5#~ zoAn_ggy<+*q{X84pT0^*E;%`Jk#_p_IjAh)2!1RJi=9I(5t%}!I+Ak9Mtz#lKBB=+ zgSo0C-7`a&>g5;%$J5TF@gX~l<~;F)Z5e{az+m(AKANbVOO~u6Be1q?T0U)U+8s|% z56&!l(-KN;?Y_~$k4iuhgl(J*(DVwlL!13iux?TG_tllB&q}1UliZq6Q{h zlGQ`f)y854r}>DWLIythi!6u>=G3R|710n!Fa^e>ziWfNIMCT8!AS1ih6wZ6)8}=m z%R`w-^}6K_?Y!w%6!4&_6PcqXfg%+2G4yxGq(J|I12;?L4w#`wpY2OkiU%-gv{g%C!KyAP z?+zkCV-6555S8?EgtAYF0AabZMs*KsA9TN4(UoD=a$p6&A)CA<`)sVWOl>}oj^ z)}1dWGf}sgx6ZMIONRwc<9M{Jo5ws~_Di&K>FO_q2=%|=h|0p_x3l%H&rm3TT%Dbc zpu(KGNMkH@SgnmSq)X?|p4bi9(B)*yQdlH!+V=-IxaY~EEA#JthkR{(bO{e`j~Z=e zuUdcOPlCZycic<@Yi-;Ho(=5T1hfcI`Mk_x#B|ABy&DqqXAX%@2;bI^j&aA$f~=;S zn@x;hxxS%u<6MdJ*6){a9O09&bI7sHXVHC-rVoAB3@X90FTUp2e4s$(Saj7Ow(Vv^ ztEVhaGxYxUUU+4meQus5e~;PYe^{Bs|7RGc0=MWQ_({TM{f{Mg|49?ce@(%v2G$nF z2LIjGRiz^Hlkq_4I#;uHK{TN*%sL&cyHIz@EJKRfsJk#`O2Z+5BAXQef%Y|IiT?I- z1B}l%UbQV5^do_|SPyBz4}E&#ss%r$(zE!uDt!od^d!fslYt@D$GCQ<44 zs>V`62knLu!fZf}`hACb*a{jWREW}njvK-i)t22WoTenY3o*2}U<`Jc;Qy z6Uh)avHd|7HPHy3K!i(@B|2?MKXLT-VNBEQ6q-w#wIOR`%zf1hnsMJ*g;I|$G-MMl zk59^FsTFb3ZU`4o(NvEk1!hR{0bxcTvSg&b2sULE_iE z7&*S3yjE*rzwvPX&6~avNndR50(-2M;I^M}y9i=-ZNTyhgg>0%2E0>>@wUtFC_^rg z?!R#0M827L4-4fsro_YQ7CfqmPuwBtC!M0koUojNJ+*@IhJ!FpXKrwhqXQ`}|us%LcX!>+y3kC)%+U+pL5^-7Gz?mJohg`~dEl6*FkrtSW zxrHxqZ3B;Q#;XQQ&?efc!-pQ!xF?!Il|Ws&6KT210)e*}h~3(6PAmL*P4nvol%RZG zyNNke(n&+KY1K}tN)p(J(xdM{oP5yV?5WQMpQ%bnxyX>TLKu=da-<6uRRBgRXtzA; zCkJxYMV!*jzRIYZyL)kkeNG{S>+#VQPt$Vg&fXF0DPToT9*e42owaQ@GqZul#clh= z>%8WpSm|)uF;Gw_cwmvlv3_>kfvVu$>kt)c3{p-$F}ngaayj2))6U zul-6NYngrgfkTbcj>HKUD_<^ut6MhzlQ!On7HU%#yzI#QKUb|Z)jF_MHQ`I7;cBnx z`5lbja?g{5)=iI9cxc>-RDy0zbb77&F1u9I&-3p4j(5ME1Z>t>Ezf3G%E9FWDDt(S z+TU-N4l;OYmkq<<&bQcWkp{{|F%{NeQrBu4?h$Cp74y z#(hg2A00<1)=YjUtbL317sqC!+;-;CPk=WY?{lKT;HrK1vVi3(obTHG^Ig^tb`4?q za}=}){lAV7u^LPNTTJagISl$g|Iu%1YW*ArA^9xV^qhz)4xwF2A5V}pXO=c|FP0Iz z(2!?69m0qRA}NL&1IpKvCw%+3IuHsfqQ5JTSyX^)5xJsA*wL%gV03ye64Z>i#PdP& z=}|a-{frv(%VuWBQ|dlDON-bD69;-r~kdr8x7LlK`a>Xi}k5*Rd! z5lb9KM$#*SvWF!FWkzaL#h4UOMkpByZ>)}xk;*IQUNk6@x?%kvJvWOqcWsG|1dYW_ z?Acqpaii_z1TDrN7^71YMQZt(==6IN$c2>TsS1(hS@W&pA%u?ods8LlPn&kahwqVz zXniP5$BK}=X@@_!_KuwZOuB7lYV@?0>YwgSl~5T{$0=V3FeDjFnXBpHth z@$D1R&}N%CPmSt~sEj1jN>5W8MOlQJ`f{RhbIwMtz zFsABA5%e@U++w(i5htppn3RcCEYi0i0cj+M0IUSq2r{RWMDw5YlsRYIwOZhUIR0?% z+t;7`b9`NynG=;-oPD`^kOJ7{$VIg8mF~CY|G}*>a6tYvKw|M{=o&>>u&yol=W!ug zzHp_3Cd21BRA-_3PLb`TkVI0XcEB8_hyi!u$C4=k#gclWq}@1By+9cxnUBUs_9FU$ zLHb?g>o;k0ZoQMZ2WF#C9j(R>h7UN8x5JYqRd@d{LkK!L_$~R;Bdl7`H|FFMYJ{7~odP=s?TC==P2o+JDd7p$p zw;N)79wA0SwW_j=HHW!axoYQDlW*BeCMyb}wqgvuH4FqpQ^5c-*fHfr5R8D~)rQk! z4$->P5nYAGGfWVmUb42q#zxq%&2pARZwa_PUm&ihPH} zI`s*O^Gg~N_Cb0;DqcX{+V7DZAZtgBsws`@ER`^&;Jq7CI86D{yeu4;+cEKeH5D)M zVu+_Hw;8X&P*ZW(Dj+Qe9BT`3Oqru;M2ugF714-S1j-Tm3Yg0<^1lH#(uC99F=E%; zbVBN74Ej!zvV!4KE{447t`jqlcvNC|=(4%a>e+2QiMy_ z3lvNvZBXkhxgzF0y1KdoZ5yGf=eKNY6y*A1Y01*1js>o=vxms8#!TQfGA`8y_H*u9 ztc!W&i7rk`R_aXuafF)&QT}EJ3vA!`r;}0PdsQ&7^YyZ1%AMm32*ZsYyeaoe!CvR6goX4-J z(RH1mo;;WY@D|$9U5`NlSr|0M^*)^WIy$eL211q z&Yt4>R^z(5V93;7jVL_t@KvD`3dW^G^TDF?GqMhDLtd<(>vh4#T#uzKHrLFw${yX5J~|0P zn6&{#NIz=L1*BA{?Mp|=-jumFkrG&maX2`r!DfT_N?X<~y0}U?;Bm&xw1zCamQ1w% zu;zT0a9=2Vp0D~Wo;5|Mywu?^>KqQrF0;scZh6JCp|R=y*=t>optj&C%I?~C z=LrGkw$nAP<6Br5OEM~EQ7RlwQJM3Abz^^FaBuB;oi6#>9A3XgBw&;UpYQlwakvs5 z;nHocY^m-9lPdb{EaGh=bs@^oKMgFvGJH)x z3Gi~6kzVZ@U)&6_tTC?w))=deF^>wllGzC9-+> z4n9_0pz~Lki?EF|`+bx?M|>69(xx(hU9W4ZoVD5Jd;9OaXY(KJjCFf^pmTyFK5acr z6QNus@!}cNHOdKYlx`~&MoZAtfuA~@&9G@5YywhP1COAP8P5gVn8k9PQLiQh_f!5* zBDQ5M|ECzOPsvbI`&CTk8_ijV`1e@Z2XD=jCDJz=a4q?ZM*?iqV)FQHY3q**h30sw zi?b^C{n(=m?(`;y{3$?~u9oz7Cby<2y zX;RHXPboj6OZVj_S;wZfsp9I1yJJeqq^v@Zch#X|2LbP`9=k5=r&)Wxj`!aZtIQe9 zMx=gS!3Vb(lZ;Pz%t^tWEitO-ouu*}+=)*&XoeZN51#<(&51JB17G0op-H&P!_%I!3Ricb6HMf@jRzUxQx=n_vp z=Gal|XP>mXb`>gtlQzG*hE@Pvnu3adBHLS^*U`Z7C7}b!elI6h`&Rq^iuIH20NbFy&%UyztVO|1khb~K-@S5zp8L4D>O2U7TGGi|b`*1omD zRu~O6ODlYl7JyZmh|)!d#E9?iXC zC~fXvAFrB$u@9}BG6zA?+FCnJu2O|^me9eW9}3sB`d<)+WzBF4`Y8Q(}ll5{0#-#V|7 zr!C_54e%(%Pdlm1r4>NLhd>Khv5;oXCPtRv-o2Z1Wj`Sv%)37gPv$V+Sv=hCn=@hS z$&F6_x_dLdGv&eFmfl&{VoLuI-HIZ&dkgd%+QD({h@$|UIVhDuvs#qFq*hRe5T;zO z4W)Mn$uAnuq{Gw*r%j*~7h#jG_6Sy;^wp1KfYfs;(jl%G4+4js7MRpS07toWhI7^F zj}}b4K{qoKkP;W=o2SJ+2qzDSOLYFmL%GX8)L6RFuS_kJ6bHb;!vCcfjRaNy2Lbc% zr+^9A2b-d`$Ammk6+*LVSzHe%+|}nB)$S>};nPQ}c&Q@I<(740;U{G(mCn~Y|7RNl zWPwG@3MzQLsU@i)I(dyboF3_GiF(li?s|XS=JX4@=(nmhrA2(0wwNY4Zx<=>c&gLm za#d784qrX(@LwNd^uJ$k(1B-iVqLE|opplpuF!#d6NYg$W@Z=wx!CnckeX03wjNf; zpFnKFBHGPy-cqO{P0T@FKB9`v)IL{23|`vj+yo?gu^x2Hk-+g)BH057>6G4+8jm0q zLVcw`0l$rZ%0Y|G(qQSGFZ8O>*S>raRk76qYX1@I; z=-BD~$duB8wHqFBqyu~5_1ZxAkEOEb0*#?|UwmN+epeEP;{eDYJv5Dr4_p+@o*&Bp zAr#Q|2O{)fFhNHNuZzHe$!rC9Ho%DJeT)BqLtiTPh=-vI!EX6W^Qd}*p&cI}`gpB3 zN%ewq9B}*SNB_YrjJKDn5LUh8L+>DvNWb}Ahh5uNaL>&PX?W)HgSD6EcaWg0q)66CUJRWMGk$c^JbJqJ8hI z=*L_=$WU4CotR!ISr0G;1WkPrWZ+*|3ZrS@T}Jm_cfnRD^xIauok}8}840M{HY#PI zS8aH6CaV!ZuR;HiA+Do{yhsea*7vi>2R#Nzww7w~LNwSc9@E{zhj8oB7t+=AW9m`oOzVRV#m;=x$ zX$Ue@k7R?_RBIVQ2)OHzXj&Q-qH`obfF|*Ch?sRiAnpE;fdT`T^mGyIqQ!MH)WWi# zt{8=9HLrehTxVOIN2nqovGk?2{x6cc@tIcVX8373V1`2+b%;JCc-_1P5h!d)wNZb4 zSc-%mt)4`&W16L_DE*ot^FD+CPP$ykPCG`PdY~J*+LJ^PzCF|hIyH|^&dX5I;ePr; zI9GX9{ArH=69m#_kH%H`oWh=b}UB#*f!-=@)7>csaFs39j#;S zt1N$e`F>{gvkqoEro>eZ z%dtM-W3|O?=e>4_0uDSGAj*ik*Hh(4pzFKcaR*p8!mxR%3fBFaoLv8o6^TD@K< zCs=W@Wf^>AT*~glvV1w%o$rH7+k)GrT@&K%`3oxDMnRQ&^+IwM)>&>$E~Q`My}jX! z?JG_kL(YQ|tYFd92X&o~*-Yb>1=4BgWD>vrEXgil(}WkbKzibg>BkKDTb)EtlWxM$MUCjhK**o(WzK^-9AWZ{?s-0Gy>5QE zWiXEMlV%4WY7K#fu3}YH`PvRD-PR-cCE!%m>)<$Nd|os%B~H89EeFDNIhq@IVy5zV zWOwNbUOS_AYuo3f_i^I)fF;$U|2$#f{m z*dV!V2b2cbXErNQWIg#(s3Dud+5)b;(hiN4*bw&nuhDz3P81O4U{C5Tq?g4;6(GM3 zPsl>ni;9?W&SUp;FGnJ-cqyNhNT1R4J}^4oGdy(Sqk_J&xm+6 zxQJtH0X%4aZ{O(iiP1VpS_$343t}C7RG%)A`4uea)W9wFst-t!=9!|GFWjXhk>r|O zA;MKqE+He$=eHROEPo`K-2*)6KwXM&tpU&C`EMZJHGyO<@%`SbMdOGu=QSB71q91Z zSF{kTYkK4Vbs?tm? zm}D^`IYDu>mZ%<=u(B@xHaiQ>-y%U(Qs~Q0>MplHUu`fgeeiPGjif%~w0*!6+rU|{ zwrH;P!D6^&bTwO6Ss2}WsP~V#P+}ElG_Voze|%zWJDj!%{*lJ#nk%?UmE% z$=pXzlOLndbLobAZEK!!O{dYy{E}4Z(3aj~`mXR?o1*p3LDs=BuUf&eYxuA^KhXc# z8YWv=-j_lF0O0>WPr=F__9o&#oUir2uYeXcZ0xXpA$(owG1e0;%8tf~SX8XYqC`3f zpYK4~&y|*!AKG#*t$LkqWkZI2Y;!YvfrEoZ)~%OP#P7NurKOpfnwil*4s+jNq)Tuy zQsHR0KiwUUlTYXHeFDA_Lq;jTyp*KCT&iP0mPp`%_mz`8$w#qFC(`drgZVuvS9(*9Of$U}|LGEu8P)t{`93rli(ZXPK+=Td`|p z>7GM!=}_nlXqsYTyt-AxfbpdIiqBH&!OW=84#i?1S@64yWxSP=zpiovgS>2TOStaV}ed7D3PXX`!e3Ky)~IKN%0!cTR~@LXp$ zzF*$MeQ^be9TR`7U1cr^5E+^O@{xrV^|hrkR05OMksISg4-Rvplq#wT9vhz7V?fUf zWN3>g8ww>zB2&aPVHE}k_MfFvOqL?fdCJq;~ef3E8Tt7#J#%6)e5V# zr>qVvLApk<`bpW+R6Wa@KQPAkiau8kqWX)$+lB6)`8(PsJ;?@E`O-NA^*s_9{L&?l z0h`m#RSm{^999RzFwYr7{)ViL7YIya$YF}FBkl?KB1y7~!-!&uOj=^t!xGC43L#PtmeFm( z0lCg65o0go%AnH?A#RK_L~^&M5xodpZ$#Xe!>ekTz>Zr-L>tz^#0JHml?v0ngdGq` z1=tb@?ZqJTT@^EWm#uNWR#4NWd~*eu{_}A<*noZ$3R25(2Y5w%(svn?p+H> zyrjsz?2!yrOvG2HQfg7iV~&c>7Y$^V4!WM&TsW1X1nMWn2p6zk8O}W3Wpg$@VWmA_ zY$#SX8h=jO^9BJ`#`R76GFi&=+uqtQ2D9akm+<1Tc7LZ?>{GKX67QnnP7ORLr*FTU z$m)WX(&4?*ab#}!c^Vt7KZSmq#L4;7QWDqX()u36nOQLk6bU38l?TJD!mo%bp5ObqyU$TgFx_6=;_h=J6vgT2z(lBJm|y zIH?zbWN6M*&o+ispID}$(6}Aee-~|aPWzHOZYn3qE`*|vw8MSH2Q9|r9B~J>ld_PD z849mDpT^8uo;LGwC$X*#YiQRQI;qq%tJRq`dby+;@6?2JY!N<$ zN7p!b;6Kjw88}1$X?Tr&cNTK>dNh5j<4=mVcXF8?J!iXpr`Jw-naW0 zy(N~|y%;VQkGgw1nMRu80>#cUA%{@GE!qW#O9|Xv2^$;TMq^X!BF`^QkkI!+%p|z{ z3}AN^Hxo;yj!ICa7a$nDy8=OXKOscI=g?qF%$dhBj-=i8?uk7qB#Oyhrqr1O&0VqA zfyyZ9mDhM(>n|Egy90-PwiFT4VHEufGP~KE#z3*9O@sjkkfsBM3?%!jI?SrordoA<Hg13p$Kcs6YMex)7+p1rPt%+EU45nem1Pck3PHWEATb~p!K;1|_JXbG@ZU^eZ( zv9ME1tv|$K$Jtt0&{s@>_oYrr$|?Bbq6ZM*KzGflDJ3Z&V&%Qk3S`a?GHp^a!C*(C!0K= zI2cs5k}4+|W=Q++QWKJ&Qw8TSedg_YcFlTt$35v+KCMC&J>B`ZySsbXySVTc;K$e3 zAd+U;e;8RJ~Wp|V4MsBEtvDtp2Y>W9h(92^7aF8HCcBX%(#yoK45+sW0k z4J)upSJ4U`XVgjH3=C+gKsN0EMP*y+{6pPSYhzx8(da_*{DNz8j9kM6Wa+Ak!S-vT zSj(B?{A83^YsRf7a4(gPQ|?gB*eID_<;lp3KY{Q}MAd`_`@`B7ySw;bRJOR~pym&i zZ4hhuLuFU}P}zn6Y)8iyjz3g(#ZcF{HGNzrKD)f7w2)i8V7klhJc(3;AGwEQN)CFA zlzTc4kMz;Fb8_zk7@>h(zLz?r%4Zq8=SVSCMS(r`0^^h5zpR z;Nzr$si?Gsfyde*@ud;Hjlzs*sC9Rqmvw9S(f(>Mik=tWlLv_sW8O}7hVmvZ^?p)+ zkc(NIfBbtXS6z#Dqc0?^4F*)A_eZh~f-=!|1ehd+UIpx5Aoda|(s3*vsY@*eP#pg; zBm|e!F;%a*|7}QM>U?k8(HGzriH7zh__}ZIo~{J56}Hgp27PaH+5H9lXYA|Q8!{#YmOdj404+<5%%*mj=}D-12zWw#LAYGY z=er1y;WZ$wo&YS0a}#lFARwjU?023Kr%^*u@~yMT{fiJos^qCJhczKbht#ZyGpPT$ z_;&ngb_VsM#Af-dOdQ*O@La!SGv?Z1px)Cp6fwDWb#`IS-s&96t(q%g*;N$k{iYQ4 zgPg|qJ;YERIKr^(YmP~4fb%Eluh{-M z^G@i`$;-2cAf|q*|I7h2>}H}9eV~b{P&{YMfTBpMCRPf~5rP@EZec3oL8m&jL*3j+ zy=kF)ud*tO^d^z-ui!uVMVrd~lZbGA@S>_m=yDP=j{dt6bsm&R1C%&u4sAzeo@U)S zm2Q(MmC}i~PueC~qh*rV$q-79yA+Fh<*9bgLD}(DM*9t~;^Q+{X-mu?njm?21dxys67KM7SFg-nfhvcYD&=zHCQ+0|}w-xKNJ7XBX z(!b1G)~Y%Yu!~#yQJo#C7taBgCczSdN_V(`EotIM@GgTkZF%EaCCf!&lMWcQU3vJ_ z(N@kIS9*Ot|HYZNT@$k@!@caapkZ4x2vrOcbCdSg95vus-ZTu`7i;9Z*BFI~c;kXMP+ z#0MC)oSc;@2QzUn?+q~*i0J9&nIv`&rQB!zWJ7~Bh}`CNvXb#<8q=KzMlOtmDI}7I zLgF^|O0i`~P5dqFJ*+@2N7wSm+1C65b;X8nH0&`^`zo{1mmh8OqbCvhu(B(TM>I+l9CMtOts#P%E!^)1giMSddaSIN=_(5JC-Oi*?q9=18T#3>t;=Z;S`IEVvQy1T?By~?!DhMGiWftAFZW4!@^LMDEP5iwT_wyWiPBsoE7KA05>}b5x zS;7_-AR(BHabFX6ugf3+en)iQs4PWZNOnO#X0w%&TPJJyofd)2)xIT}V|~)SP*+`D zl&;mmXfO0<**|J>Gd1+I#^$oxf*t5@Mn?!}=;xgCv48vK&!b;F8vxBO_Pf>RX39bj zu%)QP&_o!xWBgn4bkZD};7aI>^r2i!-M6sf+Ip6bvEZO8&7NN;RUSsoU>}CU&Klr3tbW9=P-Y;CtC(N$Pw+qP}n zwr$(CZQHhO+qSD#8LPU!y*r|JM@OG~ZruBO{(a{d`DBjF)X0Diq)ZK8uvqIHF~>@Q z@r_Pi6!WUu32U9nnpFTrO=5g?8}>-b(^!$}L=Uy9XKT89o$C4??HK0p&}R}ozY7Es z`<|L^+`!G1KapK&4>?a#*wUt;>ONIZ*;|3|^ydXCW;8RgOQoT2vV+6AY+cZ%xLc8+ zY}GPRQTjs${y~{n?8TvXz4#GXMc)jm=RW|6^+MNlD9PpM2#qmdVxu6P+3nM^*OV?< z2HJhO*}r~Ra1r)CJ!&1H^uRe;T*OH9#57vpz7(8n^YmSPl#{lJ^W+|iGjm}|z%3wI zNZZ$E4d?@8#kGM&RM=m%T0e(VX%*6PEyyR@&buB$-B4y(P#~ALZ8nfwyEPsF~ZNUN>N{_m<6#? z$QtSwO9(y~zq~LEEa^ct`C(XHAE}1iky;4i+_8Cq0bo9UDfrq=;JZ+SHm~tlP#uAg zhcMN}fdl%4(>3BLhAnYO7KsrCRX0(##>Nha?Ip(MpQyyO*>LfwIxYqZ0iV#tUSLQpFXmXu(j3W`U1WsSzHz5Q;2%*r%gBc=^JXuK;t|& z38MysROVwq7jeKREJ&eotQS(NfWAmV;}7wmhBjS>>q{FXa-goJgmxT@WW{(AJMm?M z0oVQkh}wywXv7aqtAuJ2ny*wW1XI;g1CYr?X!di9gTt)@Re#~sM<<2~P*ltz*eNQ! zpd_mwq9_frH?@eyV=5`fqqpGlruJPNx{%tn zVYs2P7DrZJyDhiGd^})cb9lT3tetA}a%RgF@FakV_a>}gGJzhOR}U?S(&`k%uY~^C zt;Mb~P_qsYxc%Ix=g!~{TfceoZ7U5Q(3Ptht^0yR!kxEgtV2kTx-1dh{_uaF5&pG_@6($K|Wp2?qZhzS;tm0ZO00xl%YYCyz(Ev!8^-$HuYf?ofVSLE2_g#Xbbr8rs3q;}{bW9UfObd)Jz zEEpHy*9=Ly=!kLlBP8x^vYJ%~F$te9k&^D4Tn`78ESej;YjW!w+?kOr ztYb5b7Qwu`stWdU`j7JV3iL9v&hGz}9jY+Ot9$tXeZ0*f`H)tHeRTPHUvykA+xybr zFI)k=irgw^;o%QobGiXJelJnv?(1&l-=) z(?|CJ*Wso>wIRNQz)k2DrPk1BrU8{0H`O_H`d~&;=W@b+vkfW8wD3yUbeZXs`5_r| z@xb7RL~vC~P#zuxs6IGVeKX+im_DD>1<33Lo!%61M+6avHQM5u6*x!L#KB6oIYlqA zpZl)d)!L|e&28}87Pz=3_t~d+GICmC%<7%#hkf!FL@1eQh0o;Wf`|OD(CMf3A!>oz z)>{IM9C4=dNt44P&33{V^MR z`PHC@$bAlnHxEyMVOzqRLiqSnpQ`O{5BJ;qbY|_w>n^Ju@A2H^I@mYV(Hd3S z!Cf|0Ll8QCBg{?l^{T-T<22&Wk$qoM=BEUv&w<1t%xlLVkPM+8sV{m3yfdj{$95JqIEsnvE(bU4uOb(j2hY4adO@ErD?>?r^hB0&L2y z_p_>0*7czftZy!M|Mk0C* zPFZ>#un3Sv9&0s zKgxDqMnKm%;$;WGDJ4Zzv7-2?7tx48P>FG3fHa%bQC1}sbwE~S`W%vSGz7^I2{NNT z71LvhHc^>pkc|%k&)M0YW7H*-tCL7M8z+ZAa}mSc*h!TlTD(h*QN{y2J@=;8=h)+; zXv%8UBed$2m~x{NbpvURR$;6qswJ-`zUcO$A)esuA6M5bu5re~c`Iu$Qz_|)^sfX) z;kP~#Fd^wML8iYx=>=MC)78E?`1>A3LNOFBaoM0~JWtSdOv#YYy(n7LLdlscjlm%KyyC#r2vEqPfIOra5u$o@`y=$P;l;mo_XVWDfELIv z0c0oVM`qkxE1YL11(#E;hXxT8cvwh-PyjXK$$1hIr9MNq5Zu&^%S-Yyg)JPzieey* z`!gun1?mdcD(WOv(#RE{kKs%Thn2{pn3&F=5Ky$Z@1yXRD;9DX(;D{)ea}*iY0W_Fj zc8#GDiRBUl~v65JZ-BR0G&RB}z$=L$4cE>k&JyRJXk&D>2yA6X||0mn^ z8ZyoJCVBudfV574s~=_-ZW72Vfbt_~seQB@ygs+>1sm+1asnl~ndJx%L7bj!nzn_% z9C=(~Cz;GhI3s5`_%xwhb#21~V7er6HzH0r8OztBL$ty~nRvF_e7UzyK|}vq_ZeBn z-^F`KBhFzNeSKknYEasm^3~QKu*i+S?}bR1o5!UTM5#-_6wOr%8oS|o0vLxaKrl<( zcMX(9#h*xwP`?K+pGcFf3|h{9U<1w>qKhb7A7yQwnux>M=h40H3r{7Jb*-XmmL*ZN z)>PKdA`jLqn$8(AcbDsvJjPet|F5K=9a%ypfG2)jGpiK6r>L{P+P&qKFK1Up_B&{Y z;uS}t!n6M90sH%o z<}G#>mG6xeeuj$;c!!PEn2V37Z86y$*(U>5j*Uvf%i;ES)qqRkw~Fu8>{B?`8@mER ziPtpbazTK%NU$$fQ~?fj*mhR%TE>D2DvL-?^Ngfwd|C_FYd(47 zw&SZYhG^HX?$OaWkDSlQ27q_kQpy1xbYRfqC0;BT{A5_b!Tztd?gHc)Svj zXyH%Q3=C%`id`#irG-HsmW3EDub3sHSj|`8r*u*5=zf>{X_(op+pVE09Eqd&>J%QH z`B&Pm>@{(?4E~jT9pMmBw57C!kdVu*+GB|mtz!HNCTefpDF?33{pvdC4mgq+9z-50 zl0=6C^-CO7#mw0za}}I{>tPCw+o#%bcEWAJ#%6##+sJ#gLRP8~N-&4q%2LF4RNc6) z!G|nU@y7dh4p7eES^US+xiYKTE3nagg-VYO5H38{h&dG%oVky;pQ4iVgKVis;=*6y z%CjS5j|oKqpUL{Ij5ILLtxlj?J1lTJYZrRlsRa4tya((}azt99%}<~T@rErjSYjUz zYa!mlJIfV{GIhPcGl%rFS5;0}7>AaG$zQ_dGedUZTDW5?8poxkH?rH~H4MLQj)Kh* z6h4@uMdJwELsx3;u<7SQxK+V8ycf}Zyl;%aKg0D;e-Hd=OIZhI*5MuqCC#zu2@dac zi9^1O+91mND*~4g(t4!tu5tZgGFOChik5)ssa)EmjH|v z|IdPFd5L+E$PHyh6nm#EbA$Lb^-X-GFkjQFpMEnQCTFhOw{F%%H@YyrUy93CO=J>~ zq^PK;zSTykId4dN59Y7+0)hSy215 z|I|BUV+;P&I}ga;-c86`DM2!C`YEAk1PTlYYIS}KP9+1* z1qzd_E^-|83;KG67t((nZ=-r);mc--xG9u`e<7+`R!K0Ai)`LAl18DXgoAjB&`2OOlUp5VtHD4>_^R&444+39lrJ00pwsIy`w#`AyEq3zV~xqT!72pyCM~ zH4>4tWVCwIL4q%u9LI(+I_+$>0c~k5PL)29ccM++L~QKc+U*bX1SwIvKeyTw!q2UC zP-*u~vw!sY8PyXUSpAo!(B&T?;5D>nl-3@pMmRmww*3xA*k-0BXX~M-#AxUuMH7L# zK9F9OUFSA2+VJ}U@|pQQpO^`aeIZL#PLY>u+I??y9q zNk>dp1fE(S;aUn%31OIPC5O5vGV+G*6Os4|A@*`53s^kxggR3Y#9xFF$7j8^tbYA^ zA&gB~+e+8^?r~HFyI)U0cdxInzhd?!P&A2EEP-fsyFHlSpNAt;1wZexw`$q7P^@Bl zOB~Eur0m^DC%yWk;@ktehJ2_`xK_OY-1?ByGRAYE_DyS;9Qs$ z)dfac;v|8JPsjw#)FP6Ae6#)x_>Jk)G!h_FeAfa(AeIP+LqdRg8q16ZAo4dH!(jiv9)$SA$VnMcA)$X zhvZ;=sgT_LOdujibl2vj*=^J6l4gjs@7mQO*gH~cC0Y*Z5@5BC&)|`?j0F@`m@(S$ z=*!1~>gVP`6~DIyu6@Fhu>s@C37&Fv5^&y zfGvQ*gUOH?hnHB`u~AQPEW~^@+vF%@#05pH`L*4J=l*)dM|>i(O^^ot73EqKT&o)P zvd$lQ!iqmy&=i|O#aH$iPclVFH%{w^k2n}Nx9T5Si_j?fgnw(1qYFM0BQC*3=c^sp zq_sM$Kp5I2hd&Dk&xogYK> zd}22?u@n~10!)?=3>^Q{#r(1lRj+@nTlg7~k!;o+keY&IZ79I=p=m`O-C4YhsV*Ux(~^XwBuTO=UIcLH$sKX0ezB4I)flnn z4+!6K@9kDKOOZd`%5y6yS2&=k>TA&x4+Kf&k!5CU(DiX4BC?liw2_V*!KsUT-Xk~r zQ&47gAc6&Wv?cztLcRTk`xsnJ)@>|T?`%!XTJkuoK&|TcBKu0)$dzvx^UEvdY9{#o zeHNFt4{^dHouCc6S29{Ylu4oAc>ds2w}InJ29-j%=f$cG^vy$fpTg6#aX_3lBfJl# ziO)%lN=Sx&b1f&-pVgN`b%~E>oMZkqdpG73tc=R?cl{`Hpctp-+}P0@Tsq`?nQ}~7 zoMDWV_=SzcqS!QaWW7Sxq+g-9qqQL1l%?diC9Ea1jA!pcnvn^&Ml8u3J0P4JXN6nA z#@URfDkmkeFD!7&y^h-Ej)=PeF)b=)*UP&}YQ2 z)h3ZP>6>pb$gqd`bz!Z-#T&04kk}`L!#s2)m;68JcTscYgl;VZ^8kfrikE6id$nO6 z+&!E05AsFZ&rvgjM-c&hidd3xq|VNvi8R4PjKk+kYHDI&Ax|5@ZN9~2&nFgE2`7C_ zO)V}m(oG^vE88HCm@Xl;oY7IP6l?)FjKdCdTU8Y+jD2bFyQkP`EMv&CM?kjYaBl&; zJ2c&4ClmKgzU71|Gi^bdG0s4=M%*gPSUmXHcLw@-#_{@N`>PZ8dHSe`Q7gUPjC>= zu5DVHb$dnmZ;K|<`bRn$`3g9Cp8X#kFl06wL&T{^u^d-}>twu}jF@kAF{@bLz@z(& zt(t9=>aBSn{q`mxSS26BehcgAV*g@AP=U{x zIS$Bm<-W^~xt7PB$T(hEnO9auQK@{#c?|A|z+6b}1Y?(goGDO#`&yjmZ6XJqbz{Fs znj@p-g&QC&@N9wkb(4|cOd76|*m7yKitd#sG-v2+AH5ANzH1xFJSx__K?P7(GX@dF zq!eEF{cN)hhI*+nFtJ_qyn_#;O}eUjLOHII(E_9(XjU-pUW;GMGiVC_tMEGYvc+? zhq~l8QlxyAeZ=T3vFc5)Tn2hfMQwlUR(*(3z6zo-c^C8>D~>qd7TC0)?J%X0@AbQA z>xk!2j&iFyMM{T9?}IE-kI4YcR&EHZvC1!-y%1Rb)Pc2cq{3>we$!(M;o3jk@eZ#J zu5w0WfEwF0OUKorMe#SdoZ0K%UxMvg2I#XB#k;TvwU~+s;s`28T)I#&zLgV)%%3Nb zk8~GI@aEA+8*w%hg0H9$jlnyFl>^?i-}<8D@WRy{64Q(SzJXYc#)q%|f%EIw{}r77 z@ASIFVb4XHiuA!0Y(vy1(WAW2}#Hj5iN$nEy&7RyCaYuv8l)kAl*{^xWy*X!x=yb=8K zg}GgW&OvJd>#x>?Wn)1^JgyP3$&wv8L!R(ME0?K2Bf398NmzOK=UC9^R1yfpgYt7y zl>NXMZ1gj1{IG_Kk@%{Rm>|WpAL!~)25MAYEITFie!!7~I!abVRy;B*WM|B2Kth}@ z7c4t7Qi&oS+5V<8N4(r&@QA6t)Z4+HUbBYr5=53bGcVbelK0*77E-yfeJ1E+hjb-z!(eNaB^zaAXCUR^vMC1mNwt)80h=IUhU zxCL$dJAim?^E^t4%BoX#P*JZ^4ogvw)>(JGpxZx!8(o=$ z?}(fr0E8U{9|8hCuz@fEt;U1q%cFem7I$z?z|11jLj&`mgi7Z|PVneP?-lMf3j`4-pGLy1*bT(=KSp++%vPkF1=jTrK_>~ zb|xXr3b3(msL`VUP2Xf)?EMM|Q;6$l(B0n8ZQvg_EeltW$0=*hhPk3Ba&^M;Qs7d2@Ls~!~vb5zhv2J;?&}i5c1J<#ivqeG`k3M#&(|gP?a#FAr+eX;c1bifDP$aav$Jc{CNV z)6o#?&T?xYO~KEZ0hDWwMvbmw{;7|{E=<8l+Lseel7FORs4W=B+h+&Y&at-2<)&0@ z-`Ul2m^lFsL0MjEjCqe~Py!^UQ%0)mQd}qFRAIX6p#hnqKh;Ii8Ec~L#c1<1^u4|I zPU{!b!EYKlSR&{X(TJ?-q`G8`I(mQE1pF?5k<5XQZ_YqZ57&_KL86I%fIUpstblrS zEdCTllrCF#yX@dGt_oQL=M3fmd zfnsb9nko$7SsXQlw`XG}&eh+gA?k_YF2=W^rM)W#Kk4w-@$(!TJ{$x!n{o2dyQBML z@fc?~qaiI~FQnF-+|Ak>wRl`!Y!%Dn5k?||S8yN z2n`!|tFC#Sr#8bV3n^UI(i~dVQ6%8d`v9@U)%>G5C}+5f_yhWxa7Z@!;Y+1L!HTxx zd5bJSeoxkC1mbME*)nRb0l!O|Fj$u{yaTa9Htn9KMSHywyCd;|6)b#h*!S1c-h71*QWLlURJ6nJ|0*$a<+~OQ>$&JZgJsj zo3?NtS`@jCaW9t;P?f6?ldszE1$k6goiWMRG3PEjDMW(e55UO}{XI%*&zkUZ%Evmk z=~KZw9Lv*onPHNZ<&p@Y-~W)A15u#8zZv7uAXJ$<>GhMA$7e{7E|E;J;^Gq(>1OA6 zTA@MXTeV?lBRz3hnPxv8Tho2vTwA$glKHOkpdRbZ!USRgTCke<)UqTn0|S=ask^`Q647hzR5nL?;!UTVz1`cjA}Nmcs8>iN9cG^w3=UZoG^O(`Q5>&=yb= z7z@R;UM)xUyC`}tjU3hv=i`E7{Anq_&YBY>S6gbYv1qtCmUI+F*Xi)+o^Z5n)v%F6 zWIV8a*Kdpc~yLO{Pp8t&E7D*=|OHEA!n&<9d@Xb^QLrWSVG(P0%;K~H|J z$H2(v-G6FJ<1C?PyR|Z`A^TKT&AZidFaQ-43wU|*%jnKb;C*|kJ$(1Q7omQzeahHk zAgN*OGJd*U7Dv<%q^q{33JJbla!RVTrIKg4s(a4Tc2>a9Z)n;;N`xc7i0Av@EmeG4l{E(YcpZ$b?xg-mVff zXV6HIn!+ls#IQtmOWMjDrHFw!y5cVJ6)$AJ9nP=|x%MY@hv%0OTE3 z=`VC+y~j!&3WgCtQc}k!Y!6&ZEHK~~PB~A*ul>PL&6j^Qci7>P;j}67a>KMiHcH)I zrU^injZ7x9g|OX?Y&=VDSyySKDhQC}fM}VPUszu|sBsOpKy3j4#p2|cu!Xa$tZI{A4jgv2- z-y1eJd$KyTK{VEwMWrX7TInMWrMjboTm$qq#@lIWj`e}inSj8hUC*69eD5B4}4OC%qlAg>CbUcbEexbB90y9 zoXNIXb>_|-lZ0XnI#btfXp`nB7Swqg>glQCR)tHf_VM2!Rn?u#I;3=nD8wjMiPLoynBwJovlU+h2(PkeNHytWVZIkyj!=M_~Ue#$mvG?thED`tMwb z3FteT8~z8BFIJMaUHo@|=7fKX!(l*Ml8F2wtPq)m95x^FL0t>!Oh9AjRgHKd#dmi4 z6Nfbr_r9Q_ zkg!`RrZNO=^95qcKCZFOsHQH@xxGe^#tsH28sFlrxhpMW_y8;ECJHTyYPsLDdN>Y6e&+}V#E8Xh z`B*+UEh#}#Iu9RwyZ4RRkHcqJXERL&7SKg{|XH>^tgq;{^d`rr*=z{na z7AO@8ht)~Ta0^k#7EV@3qO|RwfB9krw0ZY2exfsrf37qC@BcfF|7Q__f!5&P+3~dh zI?nvhAo*(LO`Ag&IPVu#s0SlZ9d@w~B}0FTRO?_9Ydn|dho(GY4SmXZ3UOh2l#knN zd~)@e1e<^$*d9BZQ!JR!#=}E+tikpQ7mcG<6d73=ANNq)-huZNL9KeEUKI;V%o27l zx#p=fWC;VL-#Xz*l7ozW-ZlL^R~AEL}mE=6a}YbP%Oi+f7$s6Cg8<))kfB zX%p#1p(%ivq#4s-SLVjbqfdwW4zJKW!MSLvwx8h4-sU+mw?-fp{IVo#IE#J^ISjQL z_Khlnx#%Bh%nO2KOw^p=M;goAz%g495IXS&Rf-m8i%^ZLirJ&mA+97Re#@%**4nAs zcl7ecrd=67hDcep>(SC8x-@F<^Tox}@e1xu7#>DqqX=<+^)3zv7(gl_nay&doPou& zN)9!m$cd;Hi_e&{OmQ4r#$H80=O1<^hsv(t_d_ox7b35^gkn4r4mKf7dc&Ov6ll#j zY)?UhFAiplW@#p)v*W?Q=P9QCoW4<0(v%mfU$Jrxo|p<{>D|8JP5y+fzOeYqSZgc{ z<#SPzl{uT9m!jr#Lc4%&Pq#<41~j5Diy_;)qu0u(L5GMn!=?YqgCQ@eqOnCE*$O?2 zE1Xhw^k)i}<#VzFY))0?)VrscWEg?KGKhM4vZ5R)dpI>7ck{|(lz ztBZy<_yZcKaOsUuiT7J+b=jiIkht8vpR4U?{g^i`56vjEF#4#|AFldM0q%q#q@BR3 zNP`{gXFpggf5L0Rmz$2}QV~iy;nc*PlG5nR@FuQPj(0TQw`cO6j`l3xBI&>{@)g$W zy%)ZBwN9L}G!x?e&NF!Y?DrwN#{)u!D@>^`E%x)^^JKJ-7<1bnf}rO_iO?hO-v3V} za6Te7MSY&5OX|Rv8|F=t6JG`ksMRLRpA*#k$o&{(0?1X`t_n6_Dx|D^G{-VY5?5`@ zh0Eb$?1(#hCX(|xmU9N+=Gmu8VQ}F98YY&PWYCVQ+KHX>=q2)H$YF5QrEo-_)`PkN zxoJ(a!#_N8Bv3>CEZakCoZV8HRGe+-YfnNm;!l3P8?#}?r&A59j5^Pxf@0M;xnTQ2 z1XP$h3FF@jU-a*=|7>HVgdO{upQ(`avyuPrQ{ktU`In=EA?-gU%Cxo)rnLHY`i5r4 zw1&3U*0wf|w1!sZ|7$7;8Jp-kTRHuop_J2VyS6{^x;`(u_G5nhHp=Xmb*aArPKaa( zruVZw_5~%la#Ex&kJteD|!n zltraE;yup^{u}2CWZb`b zT6ut7&@%msfTYU{;@?u%_i@N|#ISKD2oU`Cuq1lJ2oR5sQjXwM!EBo+;DqK<`lCl8 zo8>OxOE|d|+zr7*WHNI2qq>xuaA8$hDzhRJpR4_Pt((7vII)bpxWy#o>Cr+{3&oi{ z0%cSsTFnK5f6084qmBBZ!tyydQIJ47G2?TNvBfc$wvjL%KgKYQFEqmz1jt%98VNYG zkQD`{+rtyK{)S^t7#ODupyTk5)JGEO**G#}>rLO8**~;n!IU}KCkr@-qA5+Rw8N1h zeZ9m8NtC_wWNUx$ConJz^_gjp__b{WA>FYwC!EAVQ`N9Q9KV=WtrnGEd>K$hUe)M1 zp%?|`FwrVirigsDr(m5}N1d2~UVxs;z;uQ`r#C7>J z*Bd4I8o8C27e@td6SgL#l*6q0sQIVE@)TbeRWsw}sS|kjzG^I1!ni$xv8{kofykl4 z7h752sxe@cDNWf|>mY%s97$HSJ72uUAERqwt4nSS+zv|sq~h9;g5ojqcLMT3P#Dm7 zB!cT}YbgTZ=~a%{5rCvPAiM)Std`YWFKkxL+G(RrIg#Y*wm>A6B-w(yydA!1xkP3$ zrK)uxleT;eI}5(12Od8$92Ia{!SwI$`#S68G?wvA3m0+WDRt+x=MH_?xD1i!h7+hE zOyKxMAmtDZsOj_6_qwIPrxZo4kt6@4n+@4QxMD>QakSxG;F3zh@4@9W4>!)7U18S6 z_0O9@D<|x~Z(#TLP7gQt3#hG)Wp)Xd8g|`Y`mdO zVhKE6Z-CDNadS@8tZ67@_8YB@g)KiG1LGw}Ouo931@FVB<<1v3k2SMyky7vy+$AqN z)R@si*TMQkw=W9uB}HNh?5tr;(w47}TNy4wh z0asJ(-5mZH<$4RKhwS>%T2ZXt9F8RHeCC8IN`a&YOc;62%-QDkf|+Kv-Q1oH9d>e zMUtEfHm7`d%bKp|y*x(7In_JlL9$2NePTuSRbZH)!4` z%;@ElSNKiY{Q~At6{XLdwz94d-Ey`XD7R?vKlDOPCc&%C3$DV0rE>WO{>tI7GC-FX z$;u_m>1)Ox!LcI@$2l>VBm2{gY-dC1JmoYCcfr1@`6~C4l`2Ugj=noXB9s(33tufSt*=!#Uvo z?MqCDwY>P{ub1ry!GWwI6?>MMDPJO=&ryx2o*JL==^*L)j);DBBppvl94Cz3h^mxi zyRSTlcuNc|n{2@(qH5qWYg14v$3kvIm_5&b|J98I$1}Mp^Mhyk(EqFL#r6MYd;QOn zr2j;<*=o8!vG7p7FF#iEO<@^D_Uq>)rp^xWj`x-IPB!b>*e0n>Eo1paGS2gvU2i|J zi6kL*^T#-`0pC4Pzm>hCB2ZF0;>A!YDpd%b-0ZJ+gzrxfS4x<{B;i+E3RH_p2AEsp z_^EUR27YpcPcnj>BQKcO9MimKh7bpcT=$g1UtKV>cz7X%f*cpRaS@>EeHq+*1jtD2 zqSyqX!C~xoA7P~sLV)pFrS*E^L;93U6-|asK}Cm^Kk=^-_?&|NNVzC9gmlJPC6>iX z9z1APt($SlFrf^ZFb2~Zusks}HY1Cfsb@{_?@2>kv-pSKCMQ~fQ+<`KFvT7?jmi#fRs1|B(QV*3An~sNhMY;3{I*? z{u2I9O{lQYN}4d`Wx*ii8UjA~_um!UUGu4a-)l}2u{#!HbWK%pT1yQ|&=vGl;Xv( zDrxBhxr)jttY5R{f#-O38B^MM<|}Mf4KC|^*2DNOSPQfL&sbF>^?#fpVl?7PENLej zGQzPRaRZxTuwRU8%z8tWtr=<}sO}x#CiK+`6g;6{H$(+fO=47)>izm&aZV06!*z59 zC{+QpGkzCBHWlvWm>1|tXFAO=9|fENMU(_=?#q3M3{Vf zvwdCyH;rc3-$W9oH%-4RxDSAGtM7_8u(>mASn`u=GGK$lQP{DXPERtUDwheB_8<@Q zX_hz7#87jFtA_%XVgTXQ9-zl!rJRI02yJJsRk5xszY%k6yfB&%*#lCLKG!9Hzcj$^ z;sDFmfFrHO+t675LTuVFVqYAG6!$ze=M@!J7^+7dYhWB+cv@eftK;ZIE2ZSZj9$+G zeHRlP%@2@8Ve);~7HBQO`Z(V~+$oevRo$)e4E0*D@Hc6=Ee zhA$WE%MtPiC7fndHW~SIV3KL@!Phde>7tcqIm2m3`n@>wu^{u&k@Z}f_58(VL7dfm zE}22`+3ZPhJmWG@Jygcg=nQR&`E<)#!6xP_h72`em$G9c?dsqWf3w?#`_^)scY5e5 z^rXgF8drUk%w^AYFU_Cw?n?hdD%DlQa2FO(X4tggR)sk zw;{9*TI7TbLypjcZCYOtQp?TjktJqT)bql-BXh)!c&8Sv&}7A#(7di&2EM?KvCxwR`MbGJD!K z`a;r{n2)cG9#)&Lj6U%~6i)H}!@vT16ew{1%#*1A2xv)u-{-{K;w&dch5s<9`1{7= z-}QXpQY(PyKP$)=<^Q)8^goSL|G9#uRsa2Tn*Dh?>0}Kg2SRz8XXM8u(hO*L)@>tu zQ<$AwImQb|7Zd;s|9!zGSI8$Kl{wK%!$HvO;@pY7?cs#o!Ro_A3%`f8I?! zXFv)gXE1fm^4(v#|k<6L#bQ^a|_D-DqxiMfegiAc!KTw?KKuM zFe4jM1FTx;Mn~IikHUdK5`UUo1jAj}OFL}D2*VJ35U3z>;ttOH0p3W90(#e=At8nn z_acrj(JK~j!rd=*cum~NREI$pLBB?UeG?1DTqMG!M$x0ru!VaSvl?h1(Dmt>aYdYp z)G8iQD|Pe}(vhHd(3Em6vySz&@t4d8;ZKwYB7g5A76ctPkGUrsz1rt>`c*^#_KD-MAK$wC8`GLj%ajJ2W~;)vTUJaQT42uvas_LRnK8G|Gz zK0Zz|W1M|NY+dRHbeLqPR0qTTw70X@w+Nz0adJ7NRUT`a6fh$T9MpWG39SrJV{+}} zPoMkT{MP$x*^??Zy3`eC0Demde^@K+*M*yw%3J_gX>qMOW>N7NLsju8I3~Qj2qioF zC5dIWD<#NJSQJo>_eiNT*q`o60C?Jl_=P8r%)tEoxP86iP9PH&wHnO^3Pb@TvMNVV zwGQ16WCIqaOt`Gz1>P<4Q|yFZB%JVC@>sZ{kTgKfF4hwlff_6J#YEIS0}LS zwaB?}%Y!(JxcJNwgediQ4(Q;+?ZKPt)63oH@^E=<#HejeRK>gPh+Og_N=XUKG#am~ ziKX!7!+XaSdb5_ZT9;L_H^eLVmzUFq-HhHOo2jS>s`Cz}qP2dxF(*c;1~n^+CX;w# z*FhsOB?7|yV*DFG%Rck!=46POC|X50S~5L|3R!*rd0FqmjvRZe`BuPvEG)Qe?S3hS z>2j#21zMz1xuw1Qk8_MNUEl;LYK0p`|x=Mb27i`SP>gcA61tmjia)9L8E|e3zcn* zn5iF1Gu6CjrSG*0$zrrXX!)YlnSl9Y*e~Lh)ip0yG)lt+>k7~3*v82~r&p-IUJyZKJR!(bv|X1u5XP5a&SPJ-eTVFu19F6LxMc4adBZx; z`@WLIe-~cOscObdk>N)_pzFr?I7z>1xgWi$nJ)o^Z$4#TuC}_ z3q)m|zvFb`9ewlaIQML5DD{GNp>ZVNkzNI^@g$dpzFn#o{7rtWcK`FBTN z%RlDVeWd?tnEyAjqy85grs!(!WN7xk&GP@C!E~+!k{4k-)lu|fu9*h7O6wAwe`a}l zOZ#|&h=P4U^Y=##5_w`mGL^NTG;B37&h4n{?shzS&qY5a+UYQmX=Zg$Yia3j?})FD zUnX_1JU_xG?aZo!EL-qo*OZD-1J|Io#bFdN6Qa}a*KCKlP;L`qzg;raHh`Uw(m7Q? zQj>EdyAma?BjT$uAfxsm1-Yeu;3{Rnyej167?2vKD5`Q3}nQFMq_gBsj5h{c1pjp zcqkbWdZBx8t*8Ssn4|>Q`RV1SYiKVtYLvn^X{P9X4}inVH=>U4n$uSo@WMCISKk`oq;EWh7muElwg_wkG8pz#Dujp9<#D9;GegUO^-#b-|{RN;~Xp)`or_0{Za} zVuFf|E!;&1*-GxJx(dE`tU4Lu-pW_zt}EN5FEPg^w?!(tWgG(-Oc-88sj(O~k}}YMpRvU! z^fdQ8#Tv-XFdwR!W&|$%niHHq@ zqu#Ep^3r_(C)+<$ylm2={f;^ZpVhEk!B(O0@qj~c;rO|0eB2=87SaEA@)1TsR$_wU zVbM|55tfz@oO@A42-ou<<=6{YF=HN9lGdlh^QtU;XsOu?YiK3!5hG6fUoUnz&`OCwFJ6*Tld^MY(vwcKh0EH$>`7+ApvBBLcIpZ{Ch|g<)8dv-=HY%5=f4 zP&4otMA$fV(j^Gi(u45L>Z=7Kb7&2>D3{8*j081$=X1iHOqpjjim`wy&SC>G!qVIz zFn(lMgLZxLa!#`Z52r`vDfiFS8780wL(XKZK|DAniHAh1ZMjXBRQ+6)IM9in8ywl! zF;FbbpWh3F+X}?Z)a?m{+bhs>66{d{);~|`RDrPrx?9_;lBvkym${4zjNx8h5s;f=<*;Sqn$}~_bYy!EC)?lrqjftx?S3&Z z-d(#ul7Yt1xA6{BfW${lh)?-dv&iDUmeU|rUjdbpi<4^q(-zAx2+vmdVwXghnY*8o zlirwqWiNN5szsU6Owg1JMYfdQ<9_fj^$S~q7(KA)*DVS#?)SfvvlVG?2(^gd5iav9 zhQwr*s(S0ZYcsjIyo=QMYcjek70I20#x<2aV1UfT&0VUm%Q`J-FfA`h2XAx5RSU|P z74?J_xLr1|$7qRQ-RFYTg2X?zdo|IVA=>^Ou(t$H$^PeLTASx<^|3ags`cCn^l06z z`u(*@+#%Za9lT!zPn|19+>q&{w%m7RM~(L?UKjX773ML9_7{6cgu8em03IT3psGEA z#S&iTpx7MAM=ndJWH259$#v&Bv)zydFp?)zbLp9h{vAx_FaVBLK+DFDGS*9MY`oA* z>Fa-T371}Kk7vId$L#-sz4^aI4ZQ!!B1+KF%*DpU*7<+7j;?P+@A`L8ch+C1{^xwT zcnS-2aGW)0d#Q`&{kv_4|^i(9XClvk>MP zn9zsEbEmtEjgc|F!}DkYJ3Cw7_5l1Zl6zQUTOpqp)<|_jab+>SK@Sv#zI})y1^YoC zG!q&EhG``tDoj==e6uQ_RBm{5udiHY9{P#i@EH%h~L;(Ihqo@^xA+eBMlR$^vsLHgT zicV%6>F6Hy*}!U!tJF$t>6TfVcm+{Xqx}$5j1>d~Bna{~1$r#1s*PU7*$2+{6W5`4 zZtWOhnO1_scXXR;A}f^OWBLEb5R)cl6fGntR$v zs$Vt14gr}I%U(MbJxRbl!yaeMW)+{Up_GEe@gH9Wxf*GC$e%)2=^>q?BFFVF%wjV- zJt@Xr!s@0Xc0X3aj$UY#<402k0DGa|;MPslD{{Y_++1LoUAs@z-Mt2q!xne496vzMe(<|XS3t9pfa)o?Gol>DrKDCy%_;2LCz9Fqtz}my$#C94z zP$E=<%^pvX(4V#8E!QW)OL}oDMWhWw!1YMJhH&oowW~4a%+{ zDUqKMNE}ldm|uL@B^Ie*DtwccAe!i))_TvcA3}@Z6+e*Eu3lzZu2r-w11a*hsgz9L z6y{gQnPE*dcTXHX-uWq!J+Kg;L(3~Tiz$+evGi;kkFcynF>BWyfBMQ8-IAOwn;t?@ zIHg7_7BaAstCm}&Q&@^S^~?P|ib$!lp`&81<}@J}Ur&LVZb`!cP^j&flH@QF?aNQF z0fA>GWfkbFJ=Ciq32^orn1qhcYX#w4q_fVagv;XSi-#ryn-{^MsZx~1nbRatN>fMd zJh<6QX&1X%GDzcN}OP9SqJkgmUs%wf>`q-#h7Iao~}%5%zokCm95%n6lvqKFwEpBF6s09 zsIW1qC0{2XAhIXfO8-FE%j8vbkYk(BA8voQ6aM@L^GVu0L=K!600-r-5xtYQ#@1A? z9jb3#(m?s~FW;Vpp1ln<+CD&GJ40pNqMllH*hXrkIGGtH2JACf@Jfuc!ZyX^cUlER zzMD`-W}jo&${(6F%i(Ljss%UFb`?#I9~pWovuYFOGs-TC51|Hu?^7g&&;03#dS~)Pw{w~){c?-u z_m{*B8+RR!ehv$+_5N(L5@hn$`u2J|0<59=;f|-lrixBBILh19d=(n08t*dYsafZx{C)|Yx06fXkwlWeeIg^a(;xdpuHDk z1}T^vIc2;~t+xAR^OuM8AM@Z$_@G+Pmx5v9w8#GY00#<}vUa^wmgxF(zoxSTS{{Hy z7qBjE$Mml?(FKOb*0N`;nTW1CoqBI8AgG;a)ovAZMqvKS{Z1Jjt*nosE`{O@Z<5V% zyT_EfM@LoXwU?h6o`+M$6P{so&rh40Xz>LLKZDzDqvN;2!0NXp^>VbE9iqiHboCdi z>N^q}Emud(H3I6?6Iy7bq-kZ zT{4tNtuBAOC9sQ>%g5j4zZ~^9Rtyhkw2_zQdm1+6(v6?w1)sfXc~MoaYZ43WE6R2! zRa`r~+tF3bYEPC!aIcDK0$kK<6e2WG6w&MhF4D^JGjy{@FJCR^K6(2?)YR8?X% z#u+GlA)Tk?-Q{t|=;o!Ibia%YO+cTyY0;Z^zKyaQbGq1wO2an5fWc52W!J5Z6^Y9^hS$Ej2O>$vOXzR8_y<$m>F|4; zb`7}bXLd*dciyxs-uXL^LqqhnH#4;iS_&se#sPYex#g&cC^a7Ovf+k2!nD;K`pDlI z13$gxf0k*DR5$KIqava1xOfA{qj9)NA6K`m4Q8O%kywqk1_&uN_K{3#Ncr>0n%Dd? zVc4DEF(JuR&|qw0@XborSZ{S>d(l8dAuo&DUzkJ(Ex+cJ(a!?cQP5S^FM;qI3$>!x|4ZPdV%df4C>i9kE9^FG+wYRiv?v`y! z?;N4g*@dWoD1MoS$uH01ZVlvq?5bi@FopP@DAPT_A&@Ot&zI-ZA4k)iollR3V51Ehp2&s1E>SSI=c+?kZUOJ}k$kw!YC|tlL#-1&9HdevGRDvfl z@VgU|v2?re(>X}S%ZwdY`sPt81z=xyvOF#XBakFUsMu{Bz{Rl~QfC{wW-6s7)DR~Y z%9snc?I2GY(ZHt|{HCiW8b`c==}4j;*2rg&ge3DF^~2xHJ@pmHMVTr=Vq3EGHt=~F zF+UFAMFQnC!j#OP27_(>`wGX@ez!!l`PuC3vIgvy*+Qkd7s|>&Oh_!vwU@$NdYItb zGEFQQ3AH3>5$=N?=-c==X<8z_f_}zvFDb$7g~{853sxzF6A`KJZGhc*ILFxo3pg?H zEl@l+k0Qcn@?|ZVn1I6#chxH9rQdOD_%AgqojVt+5GNv+ba~f4Lk~gw9%@l zoRvq6o`ihAeLqndCJwh=alA&MvS$^#hSfwn^Ui4l#SIv??|Lr&{Otg3JWL(SB}=iC$vW6K>vi4p046;sUYkkdW%Z*Gg_ zq5xj`BcI4MocQ_(NA$7jC@ZauVZ&VG@6NTt8^N;qaE%S?x*U@Cu(ws?GLTnkk|>u_ z>N95XzQJs?8^CzPQ_O*5D@xpJ7nt`ue*ZT6)Ijr7a=G|~Ox=?3WxQ^22oTu91DzZ5wf}>CH>26^8YNvU*tNknEL5wz6Gq1b|JC5o_ z+rfW(w~Le%Z`1dxFZ1lSom;{ae7x`g8G3dO9U^i@o*AZQNKI6C0UIJPuL2=ijzTy~ zoun3+tS=LIT5Q}E9zUA-6icUamDaf_iHp##1WREQZ>jXKuzrr?!N)xt4#1q>1;NAy zo`U@(DF0rw_^Ewc7#T!#y5JlQS{;oXDjq=N=(Im)RG_P&)U>l8rW3?9@_el;%e<<6 zWQl8|8Xnu&Cr~FzFg+IliC?k_lk+6!l^B}~K7(ihw*aw>2VP_+A_6%LMhq9#hvM6loQ;bNX<>lRD`Xpmit7#hWi})vkR)(R!@5 zIrW*F)%@9SV6X8P_H)vkx8FrJFgI2*E@2ZgKl+l}cr(~)(>ni3w|OfU1~$1mvAKJF zNJ&0uTHXZ9RUT;iU_+T3cnhpor%el&9d*M4!B&_*EQ1REcU!-0$E|exXv#NTio~Z! z;BYkXMqv3(minZOM^$+m|KJzzEU~;Gv!izY@Q%(FOAKB!dq94ZOaB@=ErqyUt;(bL zahEXaeh_g|r;#P1=0|n+g+ob@o_9*@+cMbzbAZuYPL>7w*IqR_rx;OKGOLo1y3!;D=dBHI(WdU<=%DudVH+&Y5>? zlJx`2CFw>p*;$IICq_=3UfhxYz|FF~hxz8``XwnT_qlN+{PdR^!WEnvL`wpzpZ_Kp zF`iVXs2x=jrU~GOYDyS2byC|zHb4=n;$b6L5rr1s!jLr(yYuGAnhL|+Supvz`EZBI z?`LM-I`g$<^*zD@0P@>1M~UZ@K3>rKr)KW_>D`z=kc^VR@u8+r5oE@!_xcstg{=iO zsNly?(#DHr${W2^BT)OLHVdcAi6|88igs(Tezfi|q;E)^lMP%f4~2{Q6ud%@5Ef8R zm)Bz7NzBXSw9})|CUWLHz6U8j@X7t(wOb2WRq`ji_ir>OgWKPgmr>{{!&p1AK)awVppWRzS`bP`=` zJ=z6xnqiX+;U)s`na|DvVNC>J{w_-~8IC^Gr!1~-m#KDh8t(^p50q9k{mU?pDz?3! zueJd862Of$S*(U_Teqn2l2oKuFPHZGLJ#$M86Y+cM4 zaY4-%V=9VGp}nTAh-9`yi`K=wZ#T+HwO4rJM6?3!C1GzjGX8y3>j6HMi22U~vzF1b zQgR4Ehe@koJonW_2E*&$q&1uV2<#cCYXxOA9vxp%&adw*^Zd%%@B5Wi-TA;Zf~BLm zZ8u(@{9-J1BR&hA;(}22{0mG>pIyXV%r`P3Jr&@4(3q-5zyw z4|yN30o2k5ru@&2qyC2{sOjBRJMx)nEM`AEi_$QW&%tqG@am*ZZYRt$|AKwK9*eB7 z*VwH(tg#HOpPaVvC}`29yvkX$#&~M=Tt#vNWArv5gi34H_WeZEua7cV`*FS~R$-D5 zYD3;bGwghu$e4W0j1lB;uzK>K+hy6h;ft42tTbx~Mj|wCu5NdXD}_EUO}@CwUtT;9 zAZBYgFy;5o+b6uA$2ty~Cw>y%sD)sMLM#KqF(EaODQMDGAD`4d*sfA#SEhRu9JR*B zxuQe4Eu`l&nf=q+6$glAecP1DbDqIX40z`R0#Db`M!FQdZ8+cS&0~*r6F4>bLRuF% zTPG(usCD5t9DKGi^RGj1cGhbL^Z?TL^J@-JPir7B4rA<_*K^R>o=Q|pc!nY)Pb4Bo zL>cPL1~rlHRr}7^n(Rw44`WsKtiyggce~4l*9D-#Sl10;y)-{;zd|+n8tAz`8)}zH z#yI88`BWO4W3+s@d*3S-9X(udItyq&ANoT7Qu*YGL}7S`lU%Z^2^Y{l^npJQ%~o%x zOgHsN85MfTV(7yPV{j%jM$&_ZimQhYTux&CXzJ$lhI_uT0coWtZ<#j*Q7~@*op`&` ziw2SUF1VrKR?=VHqb#&l^Y%K}1GOgIDwi>;zyHz6wUX7uSX^JLGDk#1^DP5d+N^gP z%l0AJU1|+^9`OH(VRMULAiDryr;udue8?yDBK0+>E9BAvVwm+U(r+nYN#{H zk1Jo;|LRAYld6r_-*Of&q<`0s1pm{1^gp`L|35NTt8Y84aUl2}*RG5uN|x_BE~FaG zV@jR=aRTK^D@rVlVt_6qC&?uVwP%0bwlje?3`HQQ#sjkQ^XYiEpv^dDh@SqS!SyiW?-Drhqb zMAwf7Dz`fsIelhxY*@{t@tak`gpgT+uhkzD9pWWU-_l=NVvz|oYYfz2=#nUQzbj~Z zr209ZEG3?cI4~*4)I?N&DbUr*z~)W7p-rn=C|-!kK)Y#n>B>lt(nTSwY| z_}8!>Qw`3zeIW} zYMG@>zb1JI1z(_iHb(^Z^w_icUl(`Ykn8NZGG+|%E{qW36FBhPLI)O{*#-0y7~9=i z^P4tc3DiQ4mpIE*;J$;J42~&jbYeAvNfqF+qO==Kc?wp}nH3y^nkz-=aR{YFXG(4)OveVgl0!_cjP2^wyPDr|Xok~QpZ?lm8GtDFo zx`8^Rza(CM7j-m^rD?LS;9_Y6ALj;om$u0D@f`dKoHF=+9#*op30I%}fuFOaaeCvC z^Fl2}*;$gsjMWG5*%!M(RX^jL5 zUYBgrqxNoDhhb7T$$+u>EE;UZ@kYT*?hM(Jf3c}eGz|$6qaf6TkP+t`n|BisR7Gy| zrm}^v1Ns?(k&8i*_90Ca_wrU^PenNFSqhHI&bJn6quCZr0y(w5tO_qnV5&#fY-@yqH#CmwqA14s88txxLikj~L+xFQ^Z$vr7gjUTctbb|mhqSIdj&xPv77 zbz!wDcnCLzB;ptKJUUy>=uT>y3jH-69l)*NqE3BPX+>REHo#vmunFB#$&yOaQ_V!W zy>dcpveL%P6;x;roS69GF}79Rk3i>EcA`AF0Ez+@%Ub2Qfy5$8-eqFBEcn4`EMMyQQ}^NUv6EW}vr*Szo%}0+ zpev0oU~Dhw{u*|R0VpQY6Le}fePBR&2X$6F#@}Zr76VlGYgBTlQqXm~k!T)~nkr!N zx*NyJszH$(O*?eeoqu4^q|0ZG!rEgpF^8v#KZ{WHwMuT?%&lXiT|`dDL9&#nmC%+8vVZy=IVc?BYnqW zey1ay`9g?1)K&R@bMyuYp(+At1Cd25RA}9xtsOUX-JYW?4!^e2ef)Vja4LTnuh?^Q z-%s98PF@v%y}4lP4iwX{OVOl&P0Pvq1)nAiVeM10ghFBD{?1E2X928@e^NL53t^S9h~o2^sbU7iQ+u?pl2Px^&4Wf5;VS zJvfn1wg1UB?l6VxAMhOJK;AhimUe`F1-1guRFVaE0* zP7bC$c`X2xznz<6QA(IE!&jP0nDOKw#VbcptDOA5OR&-COmglQ`zI zzM18J>YPD^8gd~mHEBxjxakM^JI8P%n3l)555Qo_F%sZyR*4S^m)Ku2sbenl++=VI zAJ^V3pLH_f>*i?Z)Fm2ee|GN$FTi2PJ&>LjU@){|K9;c`tHPa=mL843QP(L{CP{6u z;c$BX`pzG($ifp-^oEu#Z-D<%;1`ifCz@lxbEX9b3bJ0Y5zu=Z=U`c-l%hHvWka@x z9xMq2_^px5QPq79`!ov}L&bzT>{7f(vmPpUJdJO@5=F!r4xd z@XJF(B^95(($sYTAG(B7C_tDV6(Z!e*lpn1{&hq;fKXeMXX*r4QIbDxs;n*19oN#fs z6mQMigx0hWu?c;SXb2Dym7(JcX*S}n+n31qudPgoSe{Z|les!0O?Wwb)!8rdEI@8% zn@OV|t7r0A3#_ckMc>#6Qo=GKb!p!L1F#O<1I=gcrbWjMeGKEn&@j{I zR;ujbk%G&nm%2k3QgURExT)I%*M9Q&xO6i+ojY`;-0H71IPC~ZTEHz?N7-0pBcpNC zo`BPXMHEs-fc@0Nm$qi-bZru?7Xo<6ariMBTAr8L2^H6f<`|VQPZmrQK zL{F;G?dYm^&ojoXa2cNG-4C=1R)dgP>aA^wKV{8NsW`*ET=CMXq7T_A-nvyeG|mfe z-li;iiilFIqoWnO$slsaZBVSJ4aL}hJ-=oxv;W0kJI8YsW>^G~hSlLDIZB5P+%Hy> z^L>wmUbeE%tJh<#6+mUs$%_{s(xg8hmT7;X66_-R6!@HR{00t%as{JMyRcbmFt{ z26>s%k!;|U#}u)N_$S}GWBIcG1hs?>Ewh^^hZXvCwI@N1ljRu_{RrimrVoVN+HUFs)Z4oZ$?w>_zH1L;!mB zXZ5Y&2=}ylz~dXFON(upch(V{cK>gh6;yXQtwNhuP`8}$cMvGUoB}Jqy_y6uG&a4C zS}$J(XGN^^+_?C1ECGUf?2K6#H*OZD9?ta=cOU9z`^#?asNVGpW0F60j>=PDugf2P z3#ahsEcajE*jl>@g{qZvj;y$uADTJHvhs5>hUK>CU@taTxGh>msNo zN2K-M5}Z?rLR-$s;=bHhT1B3bT@sj=>ne+TD!y3`vRy$5N1sJp59En0S)|Kn;P4aS zE=K*^=IUepUss_nWEhIU@0Y#lztuMW$D5}AcKce`n*EPA{y&?hO|@-1B6b8{I=>+R zRW?C^G|FSSVi7UFTxbSyY40YbeDW&m&QjI0wH-voU60%FQviAjiQ5?nEqrf{+3>da z_T);}{yrMcKrxk?*5uJYM1bJiEa4t~%Fn39MY{B(8{K4&;VK1AuDF?K2@hL$u$LUG zI_%uK`Cxhgw)3xr1JE^Ndz6$IE;AIvVkwO})Pr#$3yxs~uo5B1omfhGnlK!tCW<0` zk8uOai;YX<0uq#czCi`#iP@}tVSo9B6@)a{yi?|u=9lVaZMsxQr(ej$X1u4eve%~e z9=td@Ujcc63&#LQBY}In;@Pwt4~l6r z7?ndsjjYpDVQC)LzpCnSV%~@`qGEOm__3neQ4Y@a|L#8BV{|`VU%d_7?s$T>xY>Jf zcVb0>w+MTC!hh`Y{^i5ab@3-QYW+qDvzYv1_*p;3EWR0bj}=sY7bnxF%!o=^k}@O3 zw$Gpp4>(TyCJECA5vs!en52k8fi}=FN!#o+FVuy$_>9~mm_t1-JO9C(pc0|{{A;dg z^i{Jm7$dp)hWuYV;b56Jt%g6aznz*pT+53N%GbLL9zmGz5$*1^`4X{H$Do^Ae)bKg@oop>YsQce*4|Vfkd_inza%Bk3!yBLtmJCY&QX_A>D_Eh z{Av=}C6n+hTaxX~P`9OFP4Pbj6glOwi3>+R3W{1wXBXzBaWO=gNhPII;aP<DBu!(1bQ@M`)+z>@o zVNu(~L;_bs3C&JjA>j~K9G7rFa|)ziA{9;*UI=I{%i43;G-RWqR@NPwP%SGu6McNI z(a)gWlc`KT5!RZ*a4GOp8R&N<^)h7bo&t_B(uBGFj7 zT)CP|=WV;bRx#E&8MRhDt-)PByV#Z9x$qHTZg3drzOBBDA*Fi%!0CWBR9NThxVUtk z&%l)@W=jeEv#Irh=<7q^PCJEO94Eq(fG);6OL;_{vlOjP45_S3qX} zs&JR>R2h+i_3hYSc%uZ3nyw;=x$xw0y&(Ccd+AbhYx@c6kJ<96=M{tw!Bc$e#kS8? zk_h*#uwfPT16(5d$ebNvEGwN*j>9_)q>F+GtXft#{(w(PI4llG+9q6{m1T^qk}BI1 z^y1#t-a9a`F3Ay_%!dj#+km%M+3H!3q10S9r~|PRETn%J#xA7sZo=#MKBet2vS92< zp^3QDJ$P4!y+*&`so0v`GCYa3&Ahw!h@@6vGeyx|JD4nYbg#Urdb6RKwU~Xjt)uL!_7-xs*Zm!LQfg5ICFbV2%SyQD(9d zt3;vR&&kpH_JdkH4rMY}KifTT2e&LPC!HRw8o{l-xYa1Jt?qFAd6n z=2r><@B<(3(yE#TUIN+Y4IIMHuUz~Mhn(z^^!seHC*|{h=A@4PlR%GeK_dLWwIy-< z$8(Z_qmzl_Kjr=KdoKDuBz)%RIh_BRmQ!v!siU`QV3l6@W2KMo(q2Om;h$m=8BJpO zjdP&5+jbj44^Oz{pl+5&j?+N8j&YmmQYst(;RsUJ)S<2)hvD&kIC&3!xdv|?<;YT@ z8OluuWUwmZ=#I7n7>JF-0fX*zO1hTaK;AQ`X(=PMIDOXu3*%oSW5nOgc-|goyO$sl zzyubQD{L`~f>jJtP)PA8=gkadEle1alBPVML zKg@Wkg=9?S4-TyAd`&q116>Yfil6}MnM*-<6VAW@nVkBp{NmEKYq>(Mwnq?-#3V*wU z2|PlgxnSqBXY`qMpHx?_oQJ$MMxY=xVw{H|SZlkY>0V3(r$#i&Lqk+j?ByG^C6lKD2*G8Dy)IN>}o@ ztr-m=wQ0bN+ZJ3AmO}UjPTNlfT8}tBR9qdyg@)UpN`GXw@bcSGKlpEMaktnIV;fQ8 zX?*ANbb0w!i+EZz^^<7akXAZ9Tquq8dp7qbWV0WrX(5$?#pC(pOO*gR@McvIP$p15 zRFICLR9vNHLUdt?HgMboXNU^~gC3keAvc4u@2Lr`HfjBw+W@Y=NLD;_xlxUXV={C? zKUzsLIeLoJc7J>}5bpIa6>!4witYB$=3hMih8cfw7@=aVCcA>_dtK-widBZ|v!?B;Pj9#{^_1#rtdlJRwPas#3`aH^BLW9uYSw=>Z z6a8QM&NN?S0cllkQgCYq#t_ff1GFl>7^^^&oJ{76)F|3Cx3v7xrK@2F+jx2#{=<-i z4I0S}R_c|OvZ3hNdyus-9K*@z4n@ROtT5h@q&NW#@XuJ0fWm=pO^Yw%#VC=gR*a0+xrR5$S z>xq-5{WGodh;FTY0|KL`!;jWOWxJ0^lemFlPA=lpa^`xHGL2*uxNCE-R3YhFwn%b1 z)XzHlv5cF_#Am{KLm4x=YrdWJKPU0|5{5aZIK9`$n&M0b@PIZOPL?k`vA;uas|Su# z8g9UJKN206KHBTRds^Ezfq8*YqWh*#?nMxe_~dxJC4T=M2`{{1g4Uxh%X)F+XTP>@ zjgt%>L5K?5k3RJa#g4vs zVb<_g8_}+3PDCE~J~QyYNarus(?83f4Ain_}_GIM-wMI7e^x#dZqvV_|ATJ zayGDa{;v?2Y7N=gJu!r?+QzT^As(wutwGO#<19M1nZmEs$CT|R8H>`?`{X^OjOJ3QZ+d!uP?g;b|juigSz)9{8fMo z*jiA`RN49+sg&R?16L0wR8d+qP|M`f?3wBItyaSFXfPiJz+ykH%qI7!$vEnh6d84pql1^}~Pe z9;N#KY}tQ!C|Q2IUtXFv)VDIOQC0r2Y1Rg8dtQaTMSVgIJg@H&A-l*@-KrB)xAJZy zb%&Tx<_>H{*-2<3c2|Jrlq+d>mlq`rorNq`M}e6UTB(kC6vh;tUj%*Cq6_fo#`#Z$ zO@JAjGpBB1B&B0-{&WyAb;VfoJx~%0h>QBM?Lf?a~Kr2kBru_b~2f!(kD1-jZML`tOc9ZUlR(n=69cleov84Kj8y;E2E2}bDf>U-So-rI&D~OQZG9LX+>AGH_HZXJXlk=%`j|xLZwQ0=ZoRo{TO1Iji<0)* zh3BPSH(6#9S2*FZ>%4g%?p#H?z&>t1#$Ry4eqg?dGomi~)RT`U(<{3qIZFIehPl3N zrG*eVzxdgtR{vCtiRM?8^lG9}N(DVmHXIS{l%JD7OoSx_JR(g@P6e{S+o3er*_&F#k-`tTsV}ta=fqPrDvXA{^#AMr`7siz|II!?KM&S3rR`xrbkAFj~ zKckg_7w%uQm8aR5?2#ZvVy*$2EIv&hVRT*VYArw>v)qApxVAvnl-lMyG%xrWqz-&t zc2Vk{Ysv3XIO3F(J~w&iMIY@7I!++Xa?g-4H~Gz6=j(8T?54(Q%2JJI$oq)qrXv1x zzwF;B$KY_92%*FgM`N?shTe+!fk;<4Ysk#XO~jUsU0`|zy>&?upR#8$p3k0dzY8IF zEs-+uFGFfJGIr&R&l$lOYEar2D;W9LpmjRw$CN6w7_7u2@tRd_ElDF zJmlr2-99B7@_Q&wI=-LT-;T0I>CDXOa6ff|(p$i^%$bxnpLcK@w2$epj9TYoyC<5s z(^FY)h%QXsno@gFK7=I7T^Ek{77^)HUVaT&^4NekHGDP|uEp*(Tq6QlnlCZqfa2c; z9-}Vg_D>~|iUJd6_A)qMY2o+obaFPmeZPU7B(6%;Il`;yFx=o>(iK+q(SP2bYa2@V z-L3&3_bq+=*V`Y^|GKz~ZR7U%eotx}|Muekzp{?~Keb-M#QIwX_TS^$y5sj5%ZE*G z?r114ZzzF^K3~OzBtH!!FhUwoqw*~(0g38jpP7V0)c(r3%GONNiL)7UOisKAY`l0l zeBkWj1(^zj!s+$Msw&o?Va4J1^K}gK^9{L85au@E`@|k{>M~3iFv${QE)m7aN9TMB znjs!C`!&-B+VxmZ4f~QU9SmoG!5fN&sdA~*v{is`&Ji3}O&Gyi6k!CDuTS>_?(foZ zI2fWoeyAjY#(|RtWY^@~3I*PfyOq(P^3=apasjzVHG~W{f(uQHT(8BefNC|o2_Ua} z01JpwMMl?^M=Adq4w3meOZbl(6p2qlR3zw}8h=uVaWmy0tUA;|q!l4YzyhPcu4KMw zqxrAj-z&Lv1+55+3h1mugnGwiAWb7gm388im*tZp zX$UZ34*UoD_Wi58*j&2&O+V<`_K!#~dt;dUn{^STpAgCjncgXi?0cO+26)&KRJ+}2 z(D>4kXq4La8iVWrF%3i#K!{L$1r+c!g$#jEe#5jvhl^r?=}!w^d55@@ET=_Q5%?MS z!TQjXJ(agk)pCMf4NLbC+qBZx>L*=d$A_*clTqg}fLIhk-smaYh~+2(wG0}%~gH1fqnVe*!w?&I!uor3&h8j~a(23Vv- z2j5?&^*u2Rp7TW12m1cF*x7Ggx$K}+=?Atj{1TAG4U+dGh z_c7rvI2zSyQJp0{QgG6dCzJ~8jh0s@bXV+N8osf`P9|#E7*`;c)yb)Wts{X?2%JdghoDSTSN#j%dr{#E#y!`GB#Hv5j zM<%9*K>mFB_>$WX!k}R}NV#CPs@V1Nl=Jt3D|>yai9jAk(tP%&T*`(NVw;1$^O~Sn zTzLC=zYXLuk^H50hFU+HTEB*HqTQ>I{VVzgpR^y%v;nG@R!lOA-uD#AZBAHC27er# z0%sa~g4TTQvz}T$qCeWQhHSS}TlQFbvX|7lWKjY-JuGr1YLqc;^b*ueuuFLad=Iru z9+E@Ab7*Fjp}t)_a~u5PJHfIeQiKss!#i92ob^#7Is8}LreIEdo;wTV?v{8Sq@O%I zn)L#lHVX9T=_6~*c9A~qZA}*k9f1vJWek&1Q3&~~e&Ztq8#mkcF4J!ze}g2^=A}$C z#rA|FM*K0)MEIEa{nzqLzPWJ5j5Ba-&PUAf&tok%+#QljQ15gF_OHJ=ajaO(jVEDC zu3KxrUXa0YH~#dbqjLTDkr>G`*p44bwC_3)usE5@)9vxFnQo2pW_!$Z6e1pYvb*?A z&&~9E#0BLjup@ki*h$0n8@>pgJ3Z;c8~IYu;sMss`XtBVHYI<~_#o%n>%$}8UK5Va zTJ{sBFHd+l6Gy@wW6c9dLfr~AG4wF+98b+<$x)kTFFL|u4>#Ss53&XDR-mX^*?K=m zMv$5{TwvN-qS@BG>m1tst1biSpL)@L*X2g!fAN1K>>LihRKYF1@%AR7VJtvE9&(Vzq4%`>$%=fhNRj26E5Lea;H zsv?#1&O-+irXTl5oZt@o&Y2&ubVyJ63eRE1ueZ8C+Ti^@L(s4TI#IxHX5`hpLzx3? z6L{blOC@2ngdAi*keHE(TIHDr;kn+%e!iiPVNm3`U1=Xc_&a@^bUy2*t&nrB?eRt+ z$2f}hf|;0$5(kVb3n;~yM+F#XilK}E3v!RM&&M|JL1E`MMIZPZA&lh3_5T@15lK7G z8!{X4cstjxTfYR*fVd`F^M)l+U`L6o4}|ky2+wMr(#1d+Q4sqbu>=O;KS7itsQkhw zScJG%_|7AlhTz!`hww~Ql-qy&X^6#VKvooG_CR=yW-1yM*G36cwu_PN#fSBJd{THE z`xn~az_+nAdy96qi#^vXamf6n@DxW!mVCKz$ehc+68k`wuW(yD@f=zNi19`^XI{n_ z$Uw=T43gBDk5CPwQY3g#327D;mY~T!d!JSC{2<<#(Ktvc!G;pKa-5)7AuaqQ#c>SA zH7nJgg##q52Z9^O*`KrO9F1jzpx}Cg1oiBCW}2XrWdEe+1+jy(H2&NKb1N1&?RjLT zCV_&;-x=`koVf>cPg3dO9=SS9?jk0h#X`v%L}=S$F3CQ0Ne0wuooy%S&9F`4E~|;P z%Zq^udXDl%D}C3fY)Q3?Kh8_OoG<)|cOnb=3cOESr!QkPUk(Z*b4NpP1@LPN5XTwG z>H<8bhzN-Ma=FpEQh=Y*PaFcwXF?E=;u}mr?F|&?*=8AQ;}C+GkgO1ha+0z4%Mv|7 zptbiDzzZTJ^z=aHzVC+m|D}71ZePDn)HvAm^SpXC=%~2Xl2PXM`!)V60Ae8IzPOfI z(Pb2w++ox?kN76|lN?_FD(v$?nkH$eiLF-z3Rq?a|glTh&Q|h)M?@vpgdHc1TVcqG|M1}g}o_*SA`m&mGj+bH+Ef`P~_`K8CGNu5G-PdgGwwvf#!*!NCJ3Y$0oSe&*JVQq5@{xL z0_09oSyzS!t`i~ISXBMT8GbTJkvt1?d(`Kc+n#ZV6M;uD##M_DJgv%_k2<&lcU#EC zCPUI${sUTUj`E^ZM)U;%=>Lzhcj^+X&9-&Jwyg}?wr$(CZDrWDZQHgZGHja}I`OTl zz1luE)pq@bdGU@h`t1Gb@le351di%!cYJ+6dV`bF%00kb!*?(PRC(jI82^%P&B~;u)e$BrD<6TL$TTXQd zlyOwHxf3WL)q=L`U&k#bQqrLp7!P}(s8oSqV}%e9RF7@jw=Ek+uC5p4HzkZd&f<)u z$}4LS$d-&k%&Dy>TY4vwaD|G_HLbE!I+Az76;OE*9xX(SQ>s>i&=1$T8_3@2U5Cog zuO1f*5V@4r2e;vA&AaJWY4 zV`F?K9q)tcGAUcZI(ol-vGKRsCf`iHNR8sO6;udZu~9yB;ql*!31d8kzL@1VSc9~W zAUYGv77<_?o@pLZ`ot0qpyj~wb(O@jYo8GJdUfwwY?&Aio!Z#gpAfTfv(fYI*4=)$`rB*bG5@%rjA*FhP-WYzU$(9nE8ir+aQVX+`} z$6*T$dSwq2ds~b@ZunFoP34=KADy`7J9;`2Y zbSsJ|Zctw4e&jwG`X}ioyO7iDolk7$ZT?VOmXaLFZ>dTBD_m7})76e?ls(0G6S1Qd z?nv;tQ~FRWf@ZT^tSm=JCh>jMGuB1B!p+R7V$d3S^jwC`{wky#gUZQJ6#WUa`LzP$ zl*O898FN~<6gzXPK^gsT3({k*n$BWU!L9=nA6}$p>7NnRnplp@A*1lK)hu6q)t?xs zv}Z*nItic#(?B8#4F3-K%jEmnN7S@ zK4Wlq6E-?o9t_5TQ{No)ztTr`l}iZ0utjg{>RKPXIM*-l0rtV*8&zTLKb1#ri5Gv) zKAv&o;bdbeM;kzbzLgi`2O`N3K;tKd0?UpK;Wzx%v5S~0(;v6Rsg^t7P2yq=QW}~> zX(_c9sTGT+lztea9tFE0mYgNLqgjb3dzGIpf! ziR($*ynXI}F~72;?f&`~VGQ@zhU~$Fml(4+oE2sMS|P7$Dpf{H8&94hXKAh(!ZN6K zGe zKy=T*hXfx>b73WjufYa))#tv1MLcFx8tUbVQpuFm6+0W5hpLehofM7zu<8f)0&*Au zvoH1GJ=<<0BsN5sXr^k#_94*9GUqMSl()6)HDwcEluBY8Lu`1h*oIN6YoOmMJNOS|cF+r~8p6jWRrK zRI)dVF!7z(M2#tu$)3w9%2zAisI&=W?=`1?&AEQg3e_L0KBYTP(qNvMQYm>Xg@?xH zQ>@0VtC0VpGOkDJh9+}iATs~toEC9F$$^=6d}y6ee3%bUx3Tpu2ODNA^ZPMmO3G~q z<;!-{PM(AhCnLeBjCmFP?@W3~|DH{TVoYOVP4{501<4_l=lD$Eq?Ei$4*tyB+gG=V z(jZ-GbNCgWgm=SF$$6QVcc{?K)ewTI&Rb<-$c{yO(Y1ypntttcYK9T?B2(-2qKcnJN`COQt-US~-;)P8B&sXAzkLGLH9e$ypm7Mqd2+QG5seA0x&3 zQVf2q44*la@}w?$-k(12n(mO0!)cv8FTDJn(}E&HI%r4gHH(*(LpU2WOs%VAaTa&m z>)(k|PwfwzZ|MI#Ve%dsDcU~<@eY{(^n~&K7f+bd|NK7@HgR(PKS-v3op9>^6Qt~Z zSF`qkOeRkv%ho+_QyNLZmNB6~EjD(%=O-r4&xb$*plO%+@^TZ9Cl=9A*hs(CkMFx4 zy#50ZaLvOPbYf4_C`OV(1zbx@>+Aj7&o}TaLMW~t!cVZB9=n8nbAmfM9g$IlMiD0> zyfkCf|3EvS+<8+PecP#60Z{i1eK)?ZJ(KfB8)>L;%3vCgFd58o8d1nxJvMsw-~TKJtiI7~x51KD$r|OfQ;?fc^-k%VQ zB}quxH7xk*r^1@wZ&9vdjSTy0fxBME{JFiiUv)6i5oaA%djywAV`2S|SMIDK@cqU}Q6)jX z5C@Rjv0`jNXZqX&wOSCO_(=OAV#OYky%r=oMVL%=XBW5sv5w zkp%3<9ba&eQU#*q%mdNy6!U2cz=p-ycaIUTsVKrRtg3H>1QA<+&dX`0m)1|dc*k4; z76cnChoF6eJxP|%mciat3wU%J8T!-q^SM$g?FV2|i))jYmr+i2agz$J7+8xBjD+D` z-bEBrgXY^nCy2S=K7dXl7vgfI4`Ge!zrhs<|4oLn)j<;F#fQ7d;Z8Rxwwm8UzCyJ{ z9crF_gmavyNy_zxt6OxySrmGYK8d?DMz{Uj+HcL?jUQE)3pV&(_f4AyvUpdN0cB=4 z@P*&_Wv-cNgQXZ@s0U}}_W3UHP0K$tZ>KZWL41b7@NusPq+&HUR#cp7RJpiAr&wGj z?vQ9CF{XpuV#!`+f#S?UUC#e7)gCqmf_)0x=c+(@|fxiI-b;a+5kxS}sggqRH?VY#A7l zQ-+J#<=E|xLni(C*p&Pnw9;wQtLuTa^+d#AFMvv~GvYaQb8~OP3g-;vXba=~61%B% zqJ?JJ5n@zxC}%*;z%Vkcq*tfyv<*!b{)v1?Blz6 z_c?!+Cb|tisKIyXOo`sR!<586d5yxNF@Ie}`GHakuX|$u)CL;dYv`#wmJOtf;~y*3dAJ$U zi)X>6q12>}m(nbDCv?V=%DCr_I5@e+Me=J?KwIt;KV3C4fh%iw`MwWqx2uw7t8Sgr z=S#cIMY`S&-H%Y0*bKkmQdg6(g*f+aB-#76h2}uxjR(~%+_Q^$@(mg3n0{N6i1^^mS%z6}Q zpeTrMY1ScXjP5$w4LQ0$f6lwukdS|l?)j4*(eJP(g38+fVhdR)fdDXM;~7KA(^AZ7 z#{ncpDK(9$0b|kej3cS`*pmHU(Xkn#o|$0)ctC<+JIAa9kWSOgyp!Csx;S_>t5$qT zFz*z4I3rNXI5A?SgA(-I03|F7E=Wp*5T(6nQG)??_~L1RN{zQ@>d}KN5*(1|Sp%9m zM2l84jw#g7zvuRa7!i><%vwcs6VnY4Wo?1|mj;3#!2mHqc^nxbMEiM>Joqxc>>YSW zZot*lg2tVhVaA40Il9xbqDu-RA-%nXz!GB*N9PZ%bLHbOE`|6^aO4228Iwq(i%x+g zt*EDzh7-n4qo&tkVp6Ch>;=Y&x=XnOkmIB7QIX430AWPMNlmFzq$Uxroc2V+yaKQm>j9nPbkRZ7;QT zObFp0lDN>sIIc9$*I@j%{ZZx76#@QAqb)whkY2J)R06v~8upGqi}2F{lG74u~cDB_W} zyK_@Q`QKN&zZgs@Cq0P${xD>2c-Nr8x>*uyO6wjhWlV!XItxOvkR*sw**!PKzoH+e z8A4}_6tdT9WCrTuiPC>ml1&sxCbctI=* z#4)l+6G~ARz#EXqwB&&xO0hr`+Rls1LIUIy4b>-KDj#w!QK6<-4~@gu&V{$D2|WRh zJ3gej-Q;3F?XIHe4$TOr>M-CaFdRv5xno5E7O34dt~6Y3$MHSaOd(n=L7rozcLPxE z*n;rNEMWlMsqZ7~JffXjKpyuwO3W^pv+Q8(a1JmOq#wDJJ)Z+riRBv_A_3f}pnHSP zSoOR6YXi)EgkmmqP6DZ;mNR3louyzT0`{Rt-j%G}^)~J~m*Ft3EH$E!y_TZG9u9N) zM3dC?=3G~xCZSkmE1=9T*$#%2ELzb71fOtjwV>jLAUpr`2$KL7ZPPM$f3##HANDstK72(t<1 zQr2P_XUi!%3bzprWeqRgbxpA;$;;EQYZ2d^PIsE_NHs5@D@s>CroVaZt_MM zx!J*52vL@9w{+0_vNq1{F6@ zcdoWCn)9B1vCiB&@m|=WsKahRDm{&`OPE`3f{twitlVPcI|{Ng7h9pELf+E^Zt<9r ze>)xr#85CMG@9g&4XDe0pS|jLUQ7abOLIScV-zUkyo|pTfipCT>8eX~3fBW)J8;>g z+LPwttv0vW$oU5UDbfiFffxuo6MQQPbsI7pigJlWT!fiFI zT6%r-FKd;(GD`K}to6sd)a#)QhySfyvUzt^)V%!XcQO36L$2*(6t` zEhesvN`2|2SrI51E-y&*657(dVw$!YZ#v4@b&6k>D9CMMJpF-M+~INv+7a|7iL&K? z+F*?GjF?6QxRR*d;k1U#3VEaqal@0v0j4hkKQ>L&S)T z(}tBU?yXOE_P=_z(SS4Owh-XoEbk�$IA5!wd1S!W3sR%|A!Cz_fssObe950L4n& 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 T$;fdw+ZKLV$UKo9EWWl+W#wrJ literal 0 HcmV?d00001 diff --git a/lib/hsqldb-1.8.0.10.LICENSE.txt b/lib/hsqldb-1.8.0.10.LICENSE.txt new file mode 100644 index 00000000000..d45b9f8cc07 --- /dev/null +++ b/lib/hsqldb-1.8.0.10.LICENSE.txt @@ -0,0 +1,66 @@ +/* Copyright (c) 1995-2000, The Hypersonic SQL Group. + * 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 Hypersonic SQL Group 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 HYPERSONIC SQL GROUP, + * 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. + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Hypersonic SQL Group. + * + * + * For work added by the HSQL Development Group: + * + * Copyright (c) 2001-2004, The HSQL Development Group + * 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 HSQL Development Group 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG, + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + diff --git a/lib/hsqldb-1.8.0.10.jar b/lib/hsqldb-1.8.0.10.jar new file mode 100644 index 0000000000000000000000000000000000000000..e010269ddf6d6b7740cb5e7cd7cb53abf24a0add GIT binary patch literal 706710 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

Zay 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 literal 0 HcmV?d00001 diff --git a/lib/jdiff/hadoop_0.17.0.xml b/lib/jdiff/hadoop_0.17.0.xml new file mode 100644 index 00000000000..69dded31403 --- /dev/null +++ b/lib/jdiff/hadoop_0.17.0.xml @@ -0,0 +1,43272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Hadoop by default specifies two resources, loaded in-order from the + classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The balancer is a tool that balances disk space usage on an HDFS cluster + when some datanodes become full or when new empty nodes join the cluster. + The tool is deployed as an application program that can be run by the + cluster administrator on a live HDFS cluster while applications + adding and deleting files. + +

SYNOPSIS +

+ To start:
+      bin/start-balancer.sh [-threshold ]
+      Example: bin/ start-balancer.sh 
+                     start the balancer with a default threshold of 10%
+               bin/ start-balancer.sh -threshold 5
+                     start the balancer with a threshold of 5%
+ To stop:
+      bin/ stop-balancer.sh
+ 
+ +

DESCRIPTION +

The threshold parameter is a fraction in the range of (0%, 100%) with a + default value of 10%. The threshold sets a target for whether the cluster + is balanced. A cluster is balanced if for each datanode, the utilization + of the node (ratio of used space at the node to total capacity of the node) + differs from the utilization of the (ratio of used space in the cluster + to total capacity of the cluster) by no more than the threshold value. + The smaller the threshold, the more balanced a cluster will become. + It takes more time to run the balancer for small threshold values. + Also for a very small threshold the cluster may not be able to reach the + balanced state when applications write and delete files concurrently. + +

The tool moves blocks from highly utilized datanodes to poorly + utilized datanodes iteratively. In each iteration a datanode moves or + receives no more than the lesser of 10G bytes or the threshold fraction + of its capacity. Each iteration runs no more than 20 minutes. + At the end of each iteration, the balancer obtains updated datanodes + information from the namenode. + +

A system property that limits the balancer's use of bandwidth is + defined in the default configuration file: +

+ 
+   dfs.balance.bandwidthPerSec
+   1048576
+   Specifies the maximum bandwidth that each datanode 
+ can utilize for the balancing purpose in term of the number of bytes 
+ per second. 
+ 
+ 
+ +

This property determines the maximum speed at which a block will be + moved from one datanode to another. The default value is 1MB/s. The higher + the bandwidth, the faster a cluster can reach the balanced state, + but with greater competition with application processes. If an + administrator changes the value of this property in the configuration + file, the change is observed when HDFS is next restarted. + +

MONITERING BALANCER PROGRESS +

After the balancer is started, an output file name where the balancer + progress will be recorded is printed on the screen. The administrator + can monitor the running of the balancer by reading the output file. + The output shows the balancer's status iteration by iteration. In each + iteration it prints the starting time, the iteration number, the total + number of bytes that have been moved in the previous iterations, + the total number of bytes that are left to move in order for the cluster + to be balanced, and the number of bytes that are being moved in this + iteration. Normally "Bytes Already Moved" is increasing while "Bytes Left + To Move" is decreasing. + +

Running multiple instances of the balancer in an HDFS cluster is + prohibited by the tool. + +

The balancer automatically exits when any of the following five + conditions is satisfied: +

    +
  1. The cluster is balanced; +
  2. No block can be moved; +
  3. No block has been moved for five consecutive iterations; +
  4. An IOException occurs while communicating with the namenode; +
  5. Another balancer is running. +
+ +

Upon exit, a balancer returns an exit code and prints one of the + following messages to the output file in corresponding to the above exit + reasons: +

    +
  1. The cluster is balanced. Exiting +
  2. No block can be moved. Exiting... +
  3. No block has been moved for 3 iterations. Exiting... +
  4. Received an IO exception: failure reason. Exiting... +
  5. Another balancer is running. Exiting... +
+ +

The administrator can interrupt the execution of the balancer at any + time by running the command "stop-balancer.sh" on the machine where the + balancer is running.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in]]> + + + + + + + out.]]> + + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stream of bytes (of BLOCK_SIZE or less) + + This info is stored on a local disk. The DataNode + reports the table's contents to the NameNode upon startup + and every so often afterwards. + + DataNodes spend their lives in an endless loop of asking + the NameNode for something to do. A NameNode cannot connect + to a DataNode directly; a NameNode simply returns values from + functions invoked by a DataNode. + + DataNodes maintain an open server socket so that client code + or other DataNodes can read/write data. The host/port for + this server is reported to the NameNode, which then sends that + information to clients or other DataNodes that might be interested.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The tool scans all files and directories, starting from an indicated + root path. The following abnormal conditions are detected and handled:

+
    +
  • files with blocks that are completely missing from all datanodes.
    + In this case the tool can perform one of the following actions: +
      +
    • none ({@link NamenodeFsck#FIXING_NONE})
    • +
    • move corrupted files to /lost+found directory on DFS + ({@link NamenodeFsck#FIXING_MOVE}). Remaining data blocks are saved as a + block chains, representing longest consecutive series of valid blocks.
    • +
    • delete corrupted files ({@link NamenodeFsck#FIXING_DELETE})
    • +
    +
  • +
  • detect files with under-replicated or over-replicated blocks
  • +
+ Additionally, the tool collects a detailed overall DFS statistics, and + optionally can print detailed statistics on block locations and replication + factors of each file.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :/data[/] HTTP/1.1 + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :/listPaths[/][[&option]*] HTTP/1.1 + } + + Where option (default) in: + recursive ("no") + filter (".*") + exclude ("\..*\.crc") + + Response: A flat list of files/directories in the following format: + {@code + + + + + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The name-node can be started with one of the following startup options: +
    +
  • {@link FSConstants.StartupOption#REGULAR REGULAR} - normal startup
  • +
  • {@link FSConstants.StartupOption#FORMAT FORMAT} - format name node
  • +
  • {@link FSConstants.StartupOption#UPGRADE UPGRADE} - start the cluster + upgrade and create a snapshot of the current file system state
  • +
  • {@link FSConstants.StartupOption#ROLLBACK ROLLBACK} - roll the + cluster back to the previous state
  • +
+ The option is passed via configuration field: + dfs.namenode.startup + + The conf will be modified to reflect the actual ports on which + the NameNode is up and running if the user passes the port as + zero in the conf. + + @param conf confirguration + @throws IOException]]> +
+
+ + + + zero.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + datanode whose + total size is size + + @param datanode on which blocks are located + @param size total size of blocks]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + blocksequence (namespace) + 2) block->machinelist ("inodes") + + The first table is stored on disk and is very precious. + The second table is rebuilt every time the NameNode comes + up. + + 'NameNode' refers to both this class as well as the 'NameNode server'. + The 'FSNamesystem' class actually performs most of the filesystem + management. The majority of the 'NameNode' class itself is concerned + with exposing the IPC interface to the outside world, plus some + configuration management. + + NameNode implements the ClientProtocol interface, which allows + clients to ask for DFS services. ClientProtocol is not + designed for direct use by authors of DFS client code. End-users + should instead use the org.apache.nutch.hadoop.fs.FileSystem class. + + NameNode also implements the DatanodeProtocol interface, used by + DataNode programs that actually store DFS data blocks. These + methods are invoked repeatedly and automatically by all the + DataNodes in a DFS deployment. + + NameNode also implements the NamenodeProtocol interface, used by + secondary namenodes or rebalancing processes to get partial namenode's + state, for example partial blocksMap etc.]]> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The tool scans all files and directories, starting from an indicated + root path. The following abnormal conditions are detected and handled:

+
    +
  • files with blocks that are completely missing from all datanodes.
    + In this case the tool can perform one of the following actions: +
      +
    • none ({@link #FIXING_NONE})
    • +
    • move corrupted files to /lost+found directory on DFS + ({@link #FIXING_MOVE}). Remaining data blocks are saved as a + block chains, representing longest consecutive series of valid blocks.
    • +
    • delete corrupted files ({@link #FIXING_DELETE})
    • +
    +
  • +
  • detect files with under-replicated or over-replicated blocks
  • +
+ Additionally, the tool collects a detailed overall DFS statistics, and + optionally can print detailed statistics on block locations and replication + factors of each file.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #syncs}.inc()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A distributed implementation of {@link +org.apache.hadoop.fs.FileSystem}. This is loosely modelled after +Google's GFS.

+ +

The most important difference is that unlike GFS, Hadoop DFS files +have strictly one writer at any one time. Bytes are always appended +to the end of the writer's stream. There is no notion of "record appends" +or "mutations" that are then checked or reordered. Writers simply emit +a byte stream. That byte stream is guaranteed to be stored in the +order written.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #blocksRead}.inc()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        dfs.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        dfs.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically. +

+ Name Node Status info is reported in another MBean + @see org.apache.hadoop.dfs.datanode.metrics.FSDatasetMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data Node runtime statistic info is report in another MBean + @see org.apache.hadoop.dfs.datanode.metrics.DataNodeStatisticsMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name Node runtime statistic info is report in another MBean + @see org.apache.hadoop.dfs.namenode.metrics.NameNodeStatisticsMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        dfs.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        dfs.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically. +

+ Name Node Status info is report in another MBean + @see org.apache.hadoop.dfs.namenode.metrics.FSNamesystemMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link JobConf}. The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip files) are un-archived at the slave nodes. Jars maybe be + optionally added to the classpath of the tasks, a rudimentary software + distribution mechanism. Files have execution permissions. Optionally users + can also direct it to symlink the distributed cache file(s) into + the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+
+     3. Use the cached files in the {@link Mapper} or {@link Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see JobConf + @see JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ']]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f is a file, return the size of the file; + If f is a directory, return the size of the directory tree + @deprecated Use {@link #getContentSummary(Path)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is {@link DistributedFileSystem}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A client for the Kosmos filesystem (KFS) + +

Introduction

+ +This pages describes how to use Kosmos Filesystem +( KFS ) as a backing +store with Hadoop. This page assumes that you have downloaded the +KFS software and installed necessary binaries as outlined in the KFS +documentation. + +

Steps

+ +
    +
  • In the Hadoop conf directory edit hadoop-default.xml, + add the following: +
    +<property>
    +  <name>fs.kfs.impl</name>
    +  <value>org.apache.hadoop.fs.kfs.KosmosFileSystem</value>
    +  <description>The FileSystem for kfs: uris.</description>
    +</property>
    +            
    + +
  • In the Hadoop conf directory edit hadoop-site.xml, + adding the following (with appropriate values for + <server> and <port>): +
    +<property>
    +  <name>fs.default.name</name>
    +  <value>kfs://<server:port></value> 
    +</property>
    +
    +<property>
    +  <name>fs.kfs.metaServerHost</name>
    +  <value><server></value>
    +  <description>The location of the KFS meta server.</description>
    +</property>
    +
    +<property>
    +  <name>fs.kfs.metaServerPort</name>
    +  <value><port></value>
    +  <description>The location of the meta server's port.</description>
    +</property>
    +
    +
    +
  • + +
  • Copy KFS's kfs-0.1.jar to Hadoop's lib directory. This step + enables Hadoop's to load the KFS specific modules. Note + that, kfs-0.1.jar was built when you compiled KFS source + code. This jar file contains code that calls KFS's client + library code via JNI; the native code is in KFS's + libkfsClient.so library. +
  • + +
  • When the Hadoop map/reduce trackers start up, those +processes (on local as well as remote nodes) will now need to load +KFS's libkfsClient.so library. To simplify this process, it is advisable to +store libkfsClient.so in an NFS accessible directory (similar to where +Hadoop binaries/scripts are stored); then, modify Hadoop's +conf/hadoop-env.sh adding the following line and providing suitable +value for <path>: +
    +export LD_LIBRARY_PATH=<path>
    +
    + + +
  • Start only the map/reduce trackers +
    + example: execute Hadoop's bin/start-mapred.sh
  • +
+
+ +If the map/reduce job trackers start up, all file-I/O is done to KFS.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by Amazon S3. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + A distributed implementation of {@link +org.apache.hadoop.fs.FileSystem} that uses Amazon S3.

+ +

+Files are stored in S3 as blocks (represented by +{@link org.apache.hadoop.fs.s3.Block}), which have an ID and a length. +Block metadata is stored in S3 as a small record (represented by +{@link org.apache.hadoop.fs.s3.INode}) using the URL-encoded +path string as a key. Inodes record the file type (regular file or directory) and the list of blocks. +This design makes it easy to seek to any given position in a file by reading the inode data to compute +which block to access, then using S3's support for +HTTP Range headers +to start streaming from the correct position. +Renames are also efficient since only the inode is moved (by a DELETE followed by a PUT since +S3 does not support renames). +

+

+For a single file /dir1/file1 which takes two blocks of storage, the file structure in S3 +would be something like this: +

+
+/
+/dir1
+/dir1/file1
+block-6415776850131549260
+block-3026438247347758425
+
+

+Inodes start with a leading /, while blocks are prefixed with block-. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> + + + + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+ + + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ + + +A mechanism for selectively retrying methods that throw exceptions under certain circumstances. +

+ +

+Typical usage is +

+ +
+UnreliableImplementation unreliableImpl = new UnreliableImplementation();
+UnreliableInterface unreliable = (UnreliableInterface)
+  RetryProxy.create(UnreliableInterface.class, unreliableImpl,
+    RetryPolicies.retryUpToMaximumCountWithFixedSleep(4, 10, TimeUnit.SECONDS));
+unreliable.call();
+
+ +

+This will retry any method called on unreliable four times - in this case the call() +method - sleeping 10 seconds between +each retry. There are a number of {@link org.apache.hadoop.io.retry.RetryPolicies retry policies} +available, or you can implement a custom one by implementing {@link org.apache.hadoop.io.retry.RetryPolicy}. +It is also possible to specify retry policies on a +{@link org.apache.hadoop.io.retry.RetryProxy#create(Class, Object, Map) per-method basis}. +

]]> +
+ + + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + +This package provides a mechanism for using different serialization frameworks +in Hadoop. The property "io.serializations" defines a list of +{@link org.apache.hadoop.io.serializer.Serialization}s that know how to create +{@link org.apache.hadoop.io.serializer.Serializer}s and +{@link org.apache.hadoop.io.serializer.Deserializer}s. +

+ +

+To add a new serialization framework write an implementation of +{@link org.apache.hadoop.io.serializer.Serialization} and add its name to the +"io.serializations" property. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcDiscardedOps}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If the retain time is zero jobs are not persisted. +

+ A daemon thread cleans up job info files older than the retain time +

+ The retain time can be set with the 'persist.jobstatus.hours' + configuration variable (it is in hours).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides generic implementations of + {@link #validateInput(JobConf)} and {@link #getSplits(JobConf, int)}. + Implementations fo FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the taskid, say + task_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This method is used to validate the input directories when a job is + submitted so that the {@link JobClient} can fail early, with an useful + error message, in case of errors. For e.g. input directory does not exist. +

+ + @param job job configuration. + @throws InvalidInputException if the job does not have valid input]]> +
+
+ + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Note: +

+ @param dir the {@link Path} of the output directory for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + system-dir
/jobName.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+
+ + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. Typically all values are combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A software framework for easily writing applications which process vast +amounts of data (multi-terabyte data-sets) parallelly on large clusters +(thousands of nodes) built of commodity hardware in a reliable, fault-tolerant +manner.

+ +

A Map-Reduce job usually splits the input data-set into independent +chunks which processed by map tasks in completely parallel manner, +followed by reduce tasks which aggregating their output. Typically both +the input and the output of the job are stored in a +{@link org.apache.hadoop.fs.FileSystem}. The framework takes care of monitoring +tasks and re-executing failed ones. Since, usually, the compute nodes and the +storage nodes are the same i.e. Hadoop's Map-Reduce framework and Distributed +FileSystem are running on the same set of nodes, tasks are effectively scheduled +on the nodes where data is already present, resulting in very high aggregate +bandwidth across the cluster.

+ +

The Map-Reduce framework operates exclusively on <key, value> +pairs i.e. the input to the job is viewed as a set of <key, value> +pairs and the output as another, possibly different, set of +<key, value> pairs. The keys and values have to +be serializable as {@link org.apache.hadoop.io.Writable}s and additionally the +keys have to be {@link org.apache.hadoop.io.WritableComparable}s in +order to facilitate grouping by the framework.

+ +

Data flow:

+
+                                (input)
+                                <k1, v1>
+       
+                                   |
+                                   V
+       
+                                  map
+       
+                                   |
+                                   V
+
+                                <k2, v2>
+       
+                                   |
+                                   V
+       
+                                combine
+       
+                                   |
+                                   V
+       
+                                <k2, v2>
+       
+                                   |
+                                   V
+       
+                                 reduce
+       
+                                   |
+                                   V
+       
+                                <k3, v3>
+                                (output)
+
+ +

Applications typically implement +{@link org.apache.hadoop.mapred.Mapper#map(Object, Object, OutputCollector, Reporter)} +and +{@link org.apache.hadoop.mapred.Reducer#reduce(Object, Iterator, OutputCollector, Reporter)} +methods. The application-writer also specifies various facets of the job such +as input and output locations, the Partitioner, InputFormat +& OutputFormat implementations to be used etc. as +a {@link org.apache.hadoop.mapred.JobConf}. The client program, +{@link org.apache.hadoop.mapred.JobClient}, then submits the job to the framework +and optionally monitors it.

+ +

The framework spawns one map task per +{@link org.apache.hadoop.mapred.InputSplit} generated by the +{@link org.apache.hadoop.mapred.InputFormat} of the job and calls +{@link org.apache.hadoop.mapred.Mapper#map(Object, Object, OutputCollector, Reporter)} +with each <key, value> pair read by the +{@link org.apache.hadoop.mapred.RecordReader} from the InputSplit for +the task. The intermediate outputs of the maps are then grouped by keys +and optionally aggregated by combiner. The key space of intermediate +outputs are paritioned by the {@link org.apache.hadoop.mapred.Partitioner}, where +the number of partitions is exactly the number of reduce tasks for the job.

+ +

The reduce tasks fetch the sorted intermediate outputs of the maps, via http, +merge the <key, value> pairs and call +{@link org.apache.hadoop.mapred.Reducer#reduce(Object, Iterator, OutputCollector, Reporter)} +for each <key, list of values> pair. The output of the reduce tasks' is +stored on the FileSystem by the +{@link org.apache.hadoop.mapred.RecordWriter} provided by the +{@link org.apache.hadoop.mapred.OutputFormat} of the job.

+ +

Map-Reduce application to perform a distributed grep:

+

+public class Grep extends Configured implements Tool {
+
+  // map: Search for the pattern specified by 'grep.mapper.regex' &
+  //      'grep.mapper.regex.group'
+
+  class GrepMapper<K, Text> 
+  extends MapReduceBase  implements Mapper<K, Text, Text, LongWritable> {
+
+    private Pattern pattern;
+    private int group;
+
+    public void configure(JobConf job) {
+      pattern = Pattern.compile(job.get("grep.mapper.regex"));
+      group = job.getInt("grep.mapper.regex.group", 0);
+    }
+
+    public void map(K key, Text value,
+                    OutputCollector<Text, LongWritable> output,
+                    Reporter reporter)
+    throws IOException {
+      String text = value.toString();
+      Matcher matcher = pattern.matcher(text);
+      while (matcher.find()) {
+        output.collect(new Text(matcher.group(group)), new LongWritable(1));
+      }
+    }
+  }
+
+  // reduce: Count the number of occurrences of the pattern
+
+  class GrepReducer<K> extends MapReduceBase
+  implements Reducer<K, LongWritable, K, LongWritable> {
+
+    public void reduce(K key, Iterator<LongWritable> values,
+                       OutputCollector<K, LongWritable> output,
+                       Reporter reporter)
+    throws IOException {
+
+      // sum all values for this key
+      long sum = 0;
+      while (values.hasNext()) {
+        sum += values.next().get();
+      }
+
+      // output sum
+      output.collect(key, new LongWritable(sum));
+    }
+  }
+  
+  public int run(String[] args) throws Exception {
+    if (args.length < 3) {
+      System.out.println("Grep <inDir> <outDir> <regex> [<group>]");
+      ToolRunner.printGenericCommandUsage(System.out);
+      return -1;
+    }
+
+    JobConf grepJob = new JobConf(getConf(), Grep.class);
+    
+    grepJob.setJobName("grep");
+
+    grepJob.setInputPath(new Path(args[0]));
+    grepJob.setOutputPath(args[1]);
+
+    grepJob.setMapperClass(GrepMapper.class);
+    grepJob.setCombinerClass(GrepReducer.class);
+    grepJob.setReducerClass(GrepReducer.class);
+
+    grepJob.set("mapred.mapper.regex", args[2]);
+    if (args.length == 4)
+      grepJob.set("mapred.mapper.regex.group", args[3]);
+
+    grepJob.setOutputFormat(SequenceFileOutputFormat.class);
+    grepJob.setOutputKeyClass(Text.class);
+    grepJob.setOutputValueClass(LongWritable.class);
+
+    JobClient.runJob(grepJob);
+
+    return 0;
+  }
+
+  public static void main(String[] args) throws Exception {
+    int res = ToolRunner.run(new Configuration(), new Grep(), args);
+    System.exit(res);
+  }
+
+}
+
+ +

Notice how the data-flow of the above grep job is very similar to doing the +same via the unix pipeline:

+ +
+cat input/*   |   grep   |   sort    |   uniq -c   >   out
+
+ +
+      input   |    map   |  shuffle  |   reduce    >   out
+
+ +

Hadoop Map-Reduce applications need not be written in +JavaTM only. +Hadoop Streaming is a utility +which allows users to create and run jobs with any executables (e.g. shell +utilities) as the mapper and/or the reducer. +Hadoop Pipes is a +SWIG-compatible C++ API to implement +Map-Reduce applications (non JNITM based).

+ +

See Google's original +Map/Reduce paper for background information.

+ +

Java and JNI are trademarks or registered trademarks of +Sun Microsystems, Inc. in the United States and other countries.

]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Utilities for managing dependent jobs.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Given a set of sorted datasets keyed with the same class and yielding equal +partitions, it is possible to effect a join of those datasets prior to the map. +This could save costs in re-partitioning, sorting, shuffling, and writing out +data required in the general case.

+ +

Interface

+ +

The attached code offers the following interface to users of these +classes.

+ + + + + + + + + +
propertyrequiredvalue
mapred.join.expryesJoin expression to effect over input data
mapred.join.keycomparatornoWritableComparator class to use for comparing keys
mapred.join.define.<ident>noClass mapped to identifier in join expression
+ +

The join expression understands the following grammar:

+ +
func ::= <ident>([<func>,]*<func>)
+func ::= tbl(<class>,"<path>");
+
+
+ +

Operations included in this patch are partitioned into one of two types: +join operations emitting tuples and "multi-filter" operations emitting a +single value from (but not necessarily included in) a set of input values. +For a given key, each operation will consider the cross product of all +values for all sources at that node.

+ +

Identifiers supported by default:

+ + + + + + + +
identifiertypedescription
innerJoinFull inner join
outerJoinFull outer join
overrideMultiFilterFor a given key, prefer values from the rightmost source
+ +

A user of this class must set the InputFormat for the job to +CompositeInputFormat and define a join expression accepted by the +preceding grammar. For example, both of the following are acceptable:

+ +
inner(tbl(org.apache.hadoop.mapred.SequenceFileInputFormat.class,
+          "hdfs://host:8020/foo/bar"),
+      tbl(org.apache.hadoop.mapred.SequenceFileInputFormat.class,
+          "hdfs://host:8020/foo/baz"))
+
+outer(override(tbl(org.apache.hadoop.mapred.SequenceFileInputFormat.class,
+                   "hdfs://host:8020/foo/bar"),
+               tbl(org.apache.hadoop.mapred.SequenceFileInputFormat.class,
+                   "hdfs://host:8020/foo/baz")),
+      tbl(org.apache.hadoop.mapred/SequenceFileInputFormat.class,
+          "hdfs://host:8020/foo/rab"))
+
+ +

CompositeInputFormat includes a handful of convenience methods to +aid construction of these verbose statements.

+ +

As in the second example, joins may be nested. Users may provide a +comparator class in the mapred.join.keycomparator property to specify +the ordering of their keys, or accept the default comparator as returned by +WritableComparator.get(keyclass).

+ +

Users can specify their own join operations, typically by overriding +JoinRecordReader or MultiFilterRecordReader and mapping that +class to an identifier in the join expression using the +mapred.join.define.ident property, where ident is +the identifier appearing in the join expression. Users may elect to emit- or +modify- values passing through their join operation. Consulting the existing +operations for guidance is recommended. Adding arguments is considerably more +complex (and only partially supported), as one must also add a Node +type to the parse tree. One is probably better off extending +RecordReader in most cases.

+ +JIRA]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + Library of generally useful mappers, reducers, and partitioners.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aggregate framework

+

+Generally speaking, in order to implement an application using Map/Reduce +model, the developer needs to implement Map and Reduce functions (and possibly +Combine function). However, for a lot of applications related to counting and +statistics computing, these functions have very similar +characteristics. This provides a package implementing +those patterns. In particular, the package provides a generic mapper class, +a reducer class and a combiner class, and a set of built-in value aggregators. +It also provides a generic utility class, ValueAggregatorJob, that offers a static function that +creates map/reduce jobs: +

+
+public static JobConf createValueAggregatorJob(String args[]) throws IOException;
+
+
+To call this function, the user needs to pass in arguments specifying the input directories, the output directory, +the number of reducers, the input data format (textinputformat or sequencefileinputformat), and a file specifying user plugin class(es) to load by the mapper. +A user plugin class is responsible for specifying what +aggregators to use and what values are for which aggregators. +A plugin class must implement the following interface: +
+
+ public interface ValueAggregatorDescriptor { 
+     public ArrayList<Entry> generateKeyValPairs(Object key, Object value); 
+     public void configure(JobConfjob); 
+} 
+
+
+Function generateKeyValPairs will generate aggregation key/value pairs for the +input key/value pair. Each aggregation key encodes two pieces of information: the aggregation type and aggregation ID. +The value is the value to be aggregated onto the aggregation ID according to the aggregation type. Here +is a simple example user plugin class for counting the words in the input texts: +
+
+public class WordCountAggregatorDescriptor extends ValueAggregatorBaseDescriptor { 
+    public ArrayList<Entry> generateKeyValPairs(Object key, Object val) {
+        String words [] = val.toString().split(" |\t");
+        ArrayList<Entry> retv = new ArrayList<Entry>();
+        for (int i = 0; i < words.length; i++) {
+            retv.add(generateEntry(LONG_VALUE_SUM, words[i], ONE))
+        }
+        return retv;
+    }
+    public void configure(JobConf job) {}
+} 
+
+
+In the above code, LONG_VALUE_SUM is a string denoting the aggregation type LongValueSum, which sums over long values. +ONE denotes a string "1". Function generateEntry(LONG_VALUE_SUM, words[i], ONE) will inperpret the first argument as an aggregation type, the second as an aggregation ID, and the third argumnent as the value to be aggregated. The output will look like: "LongValueSum:xxxx", where XXXX is the string value of words[i]. The value will be "1". The mapper will call generateKeyValPairs(Object key, Object val) for each input key/value pair to generate the desired aggregation id/value pairs. +The down stream combiner/reducer will interpret these pairs as adding one to the aggregator XXXX. +

+Class ValueAggregatorBaseDescriptor is a base class that user plugin classes can extend. Here is the XML fragment specifying the user plugin class: +

+
+<property>
+    <name>aggregator.descriptor.num</name>
+    <value>1</value>
+</property>
+<property>
+   <name>aggregator.descriptor.0</name>
+   <value>UserDefined,org.apache.hadoop.mapred.lib.aggregate.examples.WordCountAggregatorDescriptor</value>
+</property> 
+
+
+Class ValueAggregatorBaseDescriptor itself provides a default implementation for generateKeyValPairs: +
+
+public ArrayList<Entry> generateKeyValPairs(Object key, Object val) {
+   ArrayList<Entry> retv = new ArrayList<Entry>();     
+   String countType = LONG_VALUE_SUM;
+   String id = "record_count";
+   retv.add(generateEntry(countType, id, ONE));
+   return retv;
+}
+
+
+Thus, if no user plugin class is specified, the default behavior of the map/reduce job is to count the number of records (lines) in the imput files. +

+During runtime, the mapper will invoke the generateKeyValPairs function for each input key/value pair, and emit the generated +key/value pairs: +

+
+public void map(WritableComparable key, Writable value,
+            OutputCollector output, Reporter reporter) throws IOException {
+   Iterator iter = this.aggregatorDescriptorList.iterator();
+   while (iter.hasNext()) {
+       ValueAggregatorDescriptor ad = (ValueAggregatorDescriptor) iter.next();
+       Iterator<Entry> ens = ad.generateKeyValPairs(key, value).iterator();
+       while (ens.hasNext()) {
+           Entry en = ens.next();
+           output.collect((WritableComparable)en.getKey(), (Writable)en.getValue());
+       }
+   }
+}
+
+
+The reducer will create an aggregator object for each key/value list pair, and perform the appropriate aggregation. +At the end, it will emit the aggregator's results: +
+
+public void reduce(WritableComparable key, Iterator values,
+            OutputCollector output, Reporter reporter) throws IOException {
+   String keyStr = key.toString();
+   int pos = keyStr.indexOf(ValueAggregatorDescriptor.TYPE_SEPARATOR);
+   String type = keyStr.substring(0,pos);
+   keyStr = keyStr.substring(pos+ValueAggregatorDescriptor.TYPE_SEPARATOR.length());       
+   ValueAggregator aggregator = 
+       ValueAggregatorBaseDescriptor.generateValueAggregator(type);
+   while (values.hasNext()) {
+       aggregator.addNextValue(values.next());
+   }         
+   String val = aggregator.getReport();
+   key = new Text(keyStr);
+   output.collect(key, new Text(val)); 
+}
+
+
+In order to be able to use combiner, all the aggregation type be aggregators must be associative and communitive. +The following are the types supported:
    +
  • LongValueSum: sum over long values +
  • DoubleValueSum: sum over float/double values +
  • uniqValueCount: count the number of distinct values +
  • ValueHistogram: compute the histogram of values compute the minimum, maximum, media,average, standard deviation of numeric values +
+

+

Create and run an application

+

+To create an application, the user needs to do the following things: +

+1. Implement a user plugin: +

+
+import org.apache.hadoop.mapred.lib.aggregate.ValueAggregatorBaseDescriptor;
+import org.apache.hadoop.mapred.JobConf;
+
+public class WordCountAggregatorDescriptor extends ValueAggregatorBaseDescriptor {
+   public void map(WritableComparable key, Writable value,
+            OutputCollector output, Reporter reporter) throws IOException {
+   }
+   public void configure(JobConf job) {
+    
+   } 
+}
+
+
+ +2. Create an xml file specifying the user plugin. +

+3. Compile your java class and create a jar file, say wc.jar. + +

+Finally, run the job: +

+
+        hadoop jar wc.jar org.apache.hadoop.mapred.lib.aggregate..ValueAggregatorJob indirs outdir numofreducers textinputformat|sequencefileinputformat spec_file
+
+
+

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +The class org.apache.hadoop.mapred.pipes.Submitter has a public static +method to submit a job as a JobConf and a main method that takes an +application and optional configuration file, input directories, and +output directory. The cli for the main looks like: + +

+bin/hadoop pipes \
+  [-conf path] \
+  [-input inputDir] \
+  [-output outputDir] \
+  [-jar applicationJarFile] \
+  [-inputformat class] \
+  [-map class] \
+  [-partitioner class] \
+  [-reduce class] \
+  [-writer class] \
+  [-program program url]
+
+ +

+ +The application programs link against a thin C++ wrapper library that +handles the communication with the rest of the Hadoop system. The C++ +interface is "swigable" so that interfaces can be generated for python +and other scripting languages. All of the C++ functions and classes +are in the HadoopPipes namespace. The job may consist of any +combination of Java and C++ RecordReaders, Mappers, Paritioner, +Combiner, Reducer, and RecordWriter. + +

+ +Hadoop Pipes has a generic Java class for handling the mapper and +reducer (PipesMapRunner and PipesReducer). They fork off the +application program and communicate with it over a socket. The +communication is handled by the C++ wrapper library and the +PipesMapRunner and PipesReducer. + +

+ +The application program passes in a factory object that can create +the various objects needed by the framework to the runTask +function. The framework creates the Mapper or Reducer as +appropriate and calls the map or reduce method to invoke the +application's code. The JobConf is available to the application. + +

+ +The Mapper and Reducer objects get all of their inputs, outputs, and +context via context objects. The advantage of using the context +objects is that their interface can be extended with additional +methods without breaking clients. Although this interface is different +from the current Java interface, the plan is to migrate the Java +interface in this direction. + +

+ +Although the Java implementation is typed, the C++ interfaces of keys +and values is just a byte buffer. Since STL strings provide precisely +the right functionality and are standard, they will be used. The +decision to not use stronger types was to simplify the interface. + +

+ +The application can also define combiner functions. The combiner will +be run locally by the framework in the application process to avoid +the round trip to the Java process and back. Because the compare +function is not available in C++, the combiner will use memcmp to +sort the inputs to the combiner. This is not as general as the Java +equivalent, which uses the user's comparator, but should cover the +majority of the use cases. As the map function outputs key/value +pairs, they will be buffered. When the buffer is full, it will be +sorted and passed to the combiner. The output of the combiner will be +sent to the Java process. + +

+ +The application can also set a partition function to control which key +is given to a particular reduce. If a partition function is not +defined, the Java one will be used. The partition function will be +called by the C++ framework before the key/value pair is sent back to +Java.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + +The API is abstract so that it can be implemented on top of +a variety of metrics client libraries. The choice of +client library is a configuration option, and different +modules within the same application can use +different metrics implementation libraries. +

+Sub-packages: +

+
org.apache.hadoop.metrics.spi
+
The abstract Server Provider Interface package. Those wishing to + integrate the metrics API with a particular metrics client library should + extend this package.
+ +
org.apache.hadoop.metrics.file
+
An implementation package which writes the metric data to + a file, or sends it to the standard output stream.
+ +
org.apache.hadoop.metrics.ganglia
+
An implementation package which sends metric data to + Ganglia.
+
+ +

Introduction to the Metrics API

+ +Here is a simple example of how to use this package to report a single +metric value: +
+    private ContextFactory contextFactory = ContextFactory.getFactory();
+    
+    void reportMyMetric(float myMetric) {
+        MetricsContext myContext = contextFactory.getContext("myContext");
+        MetricsRecord myRecord = myContext.getRecord("myRecord");
+        myRecord.setMetric("myMetric", myMetric);
+        myRecord.update();
+    }
+
+ +In this example there are three names: +
+
myContext
+
The context name will typically identify either the application, or else a + module within an application or library.
+ +
myRecord
+
The record name generally identifies some entity for which a set of + metrics are to be reported. For example, you could have a record named + "cacheStats" for reporting a number of statistics relating to the usage of + some cache in your application.
+ +
myMetric
+
This identifies a particular metric. For example, you might have metrics + named "cache_hits" and "cache_misses". +
+
+ +

Tags

+ +In some cases it is useful to have multiple records with the same name. For +example, suppose that you want to report statistics about each disk on a computer. +In this case, the record name would be something like "diskStats", but you also +need to identify the disk which is done by adding a tag to the record. +The code could look something like this: +
+    private MetricsRecord diskStats =
+            contextFactory.getContext("myContext").getRecord("diskStats");
+            
+    void reportDiskMetrics(String diskName, float diskBusy, float diskUsed) {
+        diskStats.setTag("diskName", diskName);
+        diskStats.setMetric("diskBusy", diskBusy);
+        diskStats.setMetric("diskUsed", diskUsed);
+        diskStats.update();
+    }
+
+ +

Buffering and Callbacks

+ +Data is not sent immediately to the metrics system when +MetricsRecord.update() is called. Instead it is stored in an +internal table, and the contents of the table are sent periodically. +This can be important for two reasons: +
    +
  1. It means that a programmer is free to put calls to this API in an + inner loop, since updates can be very frequent without slowing down + the application significantly.
  2. +
  3. Some implementations can gain efficiency by combining many metrics + into a single UDP message.
  4. +
+ +The API provides a timer-based callback via the +registerUpdater() method. The benefit of this +versus using java.util.Timer is that the callbacks will be done +immediately before sending the data, making the data as current as possible. + +

Configuration

+ +It is possible to programmatically examine and modify configuration data +before creating a context, like this: +
+    ContextFactory factory = ContextFactory.getFactory();
+    ... examine and/or modify factory attributes ...
+    MetricsContext context = factory.getContext("myContext");
+
+The factory attributes can be examined and modified using the following +ContextFactorymethods: +
    +
  • Object getAttribute(String attributeName)
  • +
  • String[] getAttributeNames()
  • +
  • void setAttribute(String name, Object value)
  • +
  • void removeAttribute(attributeName)
  • +
+ +

+ContextFactory.getFactory() initializes the factory attributes by +reading the properties file hadoop-metrics.properties if it exists +on the class path. + +

+A factory attribute named: +

+contextName.class
+
+should have as its value the fully qualified name of the class to be +instantiated by a call of the CodeFactory method +getContext(contextName). If this factory attribute is not +specified, the default is to instantiate +org.apache.hadoop.metrics.file.FileContext. + +

+Other factory attributes are specific to a particular implementation of this +API and are documented elsewhere. For example, configuration attributes for +the file and Ganglia implementations can be found in the javadoc for +their respective packages.]]> + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+ + + + +These are the implementation specific factory attributes +(See ContextFactory.getFactory()): + +
+
contextName.fileName
+
The path of the file to which metrics in context contextName + are to be appended. If this attribute is not specified, the metrics + are written to standard output by default.
+ +
contextName.period
+
The period in seconds on which the metric data is written to the + file.
+ +
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +Implementation of the metrics package that sends metric data to +Ganglia. +Programmers should not normally need to use this package directly. Instead +they should use org.hadoop.metrics. + +

+These are the implementation specific factory attributes +(See ContextFactory.getFactory()): + +

+
contextName.servers
+
Space and/or comma separated sequence of servers to which UDP + messages should be sent.
+ +
contextName.period
+
The period in seconds on which the metric data is sent to the + server(s).
+ +
contextName.units.recordName.metricName
+
The units for the specified metric in the specified record.
+ +
contextName.slope.recordName.metricName
+
The slope for the specified metric in the specified record.
+ +
contextName.tmax.recordName.metricName
+
The tmax for the specified metric in the specified record.
+ +
contextName.dmax.recordName.metricName
+
The dmax for the specified metric in the specified record.
+ +
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + org.apache.hadoop.metrics.file and +org.apache.hadoop.metrics.ganglia.

+ +Plugging in an implementation involves writing a concrete subclass of +AbstractMetricsContext. The subclass should get its + configuration information using the getAttribute(attributeName) + method.]]> + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 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++  ...
+
+ + +

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, kXML, kCSV };
+
+  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 folowing +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 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      I32;
+    double       D;
+    std::string  S;
+
+  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 I32; }
+    virtual void setI32(int32_t v) { I32 = v; }
+
+    virtual double getD(void) const { return D; }
+    virtual void setD(double v) { D = v; }
+
+    virtual std::string& getS(void) const { return S; }
+    virtual const std::string& getS(void) const { return S; }
+
+    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);
+  };
+} // 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 VF;
+    inclrec::RI        Rec;
+    std::string        Buf;
+
+  public:
+
+    R(void);
+    virtual ~R(void);
+
+    virtual bool operator==(const R& peer) const;
+    virtual bool operator<(const R& peer) const;
+
+    virtual std::vector& getVF(void) const;
+    virtual const std::vector& 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         java.lang.String
+buffer          std::string         org.apache.hadoop.record.Buffer
+class type      class type          class type
+vector    std::vector   java.util.ArrayList
+map  std::map java.util.TreeMap
+
+ +

Data encodings

+ +This section describes the format of the data encodings supported by Hadoop. +Currently, three data encodings are supported, namely binary, CSV and XML. + +

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. +
+ + +

CSV Serialization Format

+ +The CSV serialization format has a lot more structure than the "standard" +Excel CSV format, but we believe the additional structure is useful because + +
    +
  • it makes parsing a lot easier without detracting too much from legibility +
  • the delimiters around composites make it obvious when one is reading a +sequence of Hadoop records +
+ +Serialization formats for the various types are detailed in the grammar that +follows. The notable feature of the formats is the use of delimiters for +indicating the certain field types. + +
    +
  • A string field begins with a single quote ('). +
  • A buffer field begins with a sharp (#). +
  • A class, vector or map begins with 's{', 'v{' or 'm{' respectively and +ends with '}'. +
+ +The CSV format can be described by the following grammar: + +

+record = primitive / struct / vector / map
+primitive = boolean / int / long / float / double / ustring / buffer
+
+boolean = "T" / "F"
+int = ["-"] 1*DIGIT
+long = ";" ["-"] 1*DIGIT
+float = ["-"] 1*DIGIT "." 1*DIGIT ["E" / "e" ["-"] 1*DIGIT]
+double = ";" ["-"] 1*DIGIT "." 1*DIGIT ["E" / "e" ["-"] 1*DIGIT]
+
+ustring = "'" *(UTF8 char except NULL, LF, % and , / "%00" / "%0a" / "%25" / "%2c" )
+
+buffer = "#" *(BYTE except NULL, LF, % and , / "%00" / "%0a" / "%25" / "%2c" )
+
+struct = "s{" record *("," record) "}"
+vector = "v{" [record *("," record)] "}"
+map = "m{" [*(record "," record)] "}"
+
+ +

XML Serialization Format

+ +The XML serialization format is the same used by Apache XML-RPC +(http://ws.apache.org/xmlrpc/types.html). This is an extension of the original +XML-RPC format and adds some additional data types. All record I/O types are +not directly expressible in this format, and access to a DDL is required in +order to convert these to valid types. All types primitive or composite are +represented by <value> elements. The particular XML-RPC type is +indicated by a nested element in the <value> element. The encoding for +records is always UTF-8. Primitive types are serialized as follows: + +
    +
  • byte: XML tag <ex:i1>. Values: 1-byte unsigned +integers represented in US-ASCII +
  • boolean: XML tag <boolean>. Values: "0" or "1" +
  • int: XML tags <i4> or <int>. Values: 4-byte +signed integers represented in US-ASCII. +
  • long: XML tag <ex:i8>. Values: 8-byte signed integers +represented in US-ASCII. +
  • float: XML tag <ex:float>. Values: Single precision +floating point numbers represented in US-ASCII. +
  • double: XML tag <double>. Values: Double precision +floating point numbers represented in US-ASCII. +
  • ustring: XML tag <;string>. Values: String values +represented as UTF-8. XML does not permit all Unicode characters in literal +data. In particular, NULLs and control chars are not allowed. Additionally, +XML processors are required to replace carriage returns with line feeds and to +replace CRLF sequences with line feeds. Programming languages that we work +with do not impose these restrictions on string types. To work around these +restrictions, disallowed characters and CRs are percent escaped in strings. +The '%' character is also percent escaped. +
  • buffer: XML tag <string&>. Values: Arbitrary binary +data. Represented as hexBinary, each byte is replaced by its 2-byte +hexadecimal representation. +
+ +Composite types are serialized as follows: + +
    +
  • class: XML tag <struct>. A struct is a sequence of +<member> elements. Each <member> element has a <name> +element and a <value> element. The <name> is a string that must +match /[a-zA-Z][a-zA-Z0-9_]*/. The value of the member is represented +by a <value> element. + +
  • vector: XML tag <array<. An <array> contains a +single <data> element. The <data> element is a sequence of +<value> elements each of which represents an element of the vector. + +
  • map: XML tag <array>. Same as vector. + +
+ +For example: + +

+class {
+  int           MY_INT;            // value 5
+  vector MY_VEC;            // values 0.1, -0.89, 2.45e4
+  buffer        MY_BUF;            // value '\00\n\tabc%'
+}
+
+ +is serialized as + +

+<value>
+  <struct>
+    <member>
+      <name>MY_INT</name>
+      <value><i4>5</i4></value>
+    </member>
+    <member>
+      <name>MY_VEC</name>
+      <value>
+        <array>
+          <data>
+            <value><ex:float>0.1</ex:float></value>
+            <value><ex:float>-0.89</ex:float></value>
+            <value><ex:float>2.45e4</ex:float></value>
+          </data>
+        </array>
+      </value>
+    </member>
+    <member>
+      <name>MY_BUF</name>
+      <value><string>%00\n\tabc%25</string></value>
+    </member>
+  </struct>
+</value> 
+
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 job job configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/lib/jdiff/hadoop_0.18.1.xml b/lib/jdiff/hadoop_0.18.1.xml new file mode 100644 index 00000000000..fd844cbed0f --- /dev/null +++ b/lib/jdiff/hadoop_0.18.1.xml @@ -0,0 +1,44778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Hadoop by default specifies two resources, loaded in-order from the + classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The balancer is a tool that balances disk space usage on an HDFS cluster + when some datanodes become full or when new empty nodes join the cluster. + The tool is deployed as an application program that can be run by the + cluster administrator on a live HDFS cluster while applications + adding and deleting files. + +

SYNOPSIS +

+ To start:
+      bin/start-balancer.sh [-threshold ]
+      Example: bin/ start-balancer.sh 
+                     start the balancer with a default threshold of 10%
+               bin/ start-balancer.sh -threshold 5
+                     start the balancer with a threshold of 5%
+ To stop:
+      bin/ stop-balancer.sh
+ 
+ +

DESCRIPTION +

The threshold parameter is a fraction in the range of (0%, 100%) with a + default value of 10%. The threshold sets a target for whether the cluster + is balanced. A cluster is balanced if for each datanode, the utilization + of the node (ratio of used space at the node to total capacity of the node) + differs from the utilization of the (ratio of used space in the cluster + to total capacity of the cluster) by no more than the threshold value. + The smaller the threshold, the more balanced a cluster will become. + It takes more time to run the balancer for small threshold values. + Also for a very small threshold the cluster may not be able to reach the + balanced state when applications write and delete files concurrently. + +

The tool moves blocks from highly utilized datanodes to poorly + utilized datanodes iteratively. In each iteration a datanode moves or + receives no more than the lesser of 10G bytes or the threshold fraction + of its capacity. Each iteration runs no more than 20 minutes. + At the end of each iteration, the balancer obtains updated datanodes + information from the namenode. + +

A system property that limits the balancer's use of bandwidth is + defined in the default configuration file: +

+ 
+   dfs.balance.bandwidthPerSec
+   1048576
+   Specifies the maximum bandwidth that each datanode 
+ can utilize for the balancing purpose in term of the number of bytes 
+ per second. 
+ 
+ 
+ +

This property determines the maximum speed at which a block will be + moved from one datanode to another. The default value is 1MB/s. The higher + the bandwidth, the faster a cluster can reach the balanced state, + but with greater competition with application processes. If an + administrator changes the value of this property in the configuration + file, the change is observed when HDFS is next restarted. + +

MONITERING BALANCER PROGRESS +

After the balancer is started, an output file name where the balancer + progress will be recorded is printed on the screen. The administrator + can monitor the running of the balancer by reading the output file. + The output shows the balancer's status iteration by iteration. In each + iteration it prints the starting time, the iteration number, the total + number of bytes that have been moved in the previous iterations, + the total number of bytes that are left to move in order for the cluster + to be balanced, and the number of bytes that are being moved in this + iteration. Normally "Bytes Already Moved" is increasing while "Bytes Left + To Move" is decreasing. + +

Running multiple instances of the balancer in an HDFS cluster is + prohibited by the tool. + +

The balancer automatically exits when any of the following five + conditions is satisfied: +

    +
  1. The cluster is balanced; +
  2. No block can be moved; +
  3. No block has been moved for five consecutive iterations; +
  4. An IOException occurs while communicating with the namenode; +
  5. Another balancer is running. +
+ +

Upon exit, a balancer returns an exit code and prints one of the + following messages to the output file in corresponding to the above exit + reasons: +

    +
  1. The cluster is balanced. Exiting +
  2. No block can be moved. Exiting... +
  3. No block has been moved for 3 iterations. Exiting... +
  4. Received an IO exception: failure reason. Exiting... +
  5. Another balancer is running. Exiting... +
+ +

The administrator can interrupt the execution of the balancer at any + time by running the command "stop-balancer.sh" on the machine where the + balancer is running.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in]]> + + + + + + + out.]]> + + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stream of bytes (of BLOCK_SIZE or less) + + This info is stored on a local disk. The DataNode + reports the table's contents to the NameNode upon startup + and every so often afterwards. + + DataNodes spend their lives in an endless loop of asking + the NameNode for something to do. A NameNode cannot connect + to a DataNode directly; a NameNode simply returns values from + functions invoked by a DataNode. + + DataNodes maintain an open server socket so that client code + or other DataNodes can read/write data. The host/port for + this server is reported to the NameNode, which then sends that + information to clients or other DataNodes that might be interested.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The tool scans all files and directories, starting from an indicated + root path. The following abnormal conditions are detected and handled:

+
    +
  • files with blocks that are completely missing from all datanodes.
    + In this case the tool can perform one of the following actions: +
      +
    • none ({@link NamenodeFsck#FIXING_NONE})
    • +
    • move corrupted files to /lost+found directory on DFS + ({@link NamenodeFsck#FIXING_MOVE}). Remaining data blocks are saved as a + block chains, representing longest consecutive series of valid blocks.
    • +
    • delete corrupted files ({@link NamenodeFsck#FIXING_DELETE})
    • +
    +
  • +
  • detect files with under-replicated or over-replicated blocks
  • +
+ Additionally, the tool collects a detailed overall DFS statistics, and + optionally can print detailed statistics on block locations and replication + factors of each file. + The tool also provides and option to filter open files during the scan.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :/data[/] HTTP/1.1 + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #filesTotal}.set()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :/listPaths[/][[&option]*] HTTP/1.1 + } + + Where option (default) in: + recursive ("no") + filter (".*") + exclude ("\..*\.crc") + + Response: A flat list of files/directories in the following format: + {@code +

+ + + + }]]> +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The name-node can be started with one of the following startup options: +
    +
  • {@link FSConstants.StartupOption#REGULAR REGULAR} - normal startup
  • +
  • {@link FSConstants.StartupOption#FORMAT FORMAT} - format name node
  • +
  • {@link FSConstants.StartupOption#UPGRADE UPGRADE} - start the cluster + upgrade and create a snapshot of the current file system state
  • +
  • {@link FSConstants.StartupOption#ROLLBACK ROLLBACK} - roll the + cluster back to the previous state
  • +
+ The option is passed via configuration field: + dfs.namenode.startup + + The conf will be modified to reflect the actual ports on which + the NameNode is up and running if the user passes the port as + zero in the conf. + + @param conf confirguration + @throws IOException]]> +
+
+ + + + zero.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + datanode whose + total size is size + + @param datanode on which blocks are located + @param size total size of blocks]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + blocksequence (namespace) + 2) block->machinelist ("inodes") + + The first table is stored on disk and is very precious. + The second table is rebuilt every time the NameNode comes + up. + + 'NameNode' refers to both this class as well as the 'NameNode server'. + The 'FSNamesystem' class actually performs most of the filesystem + management. The majority of the 'NameNode' class itself is concerned + with exposing the IPC interface to the outside world, plus some + configuration management. + + NameNode implements the ClientProtocol interface, which allows + clients to ask for DFS services. ClientProtocol is not + designed for direct use by authors of DFS client code. End-users + should instead use the org.apache.nutch.hadoop.fs.FileSystem class. + + NameNode also implements the DatanodeProtocol interface, used by + DataNode programs that actually store DFS data blocks. These + methods are invoked repeatedly and automatically by all the + DataNodes in a DFS deployment. + + NameNode also implements the NamenodeProtocol interface, used by + secondary namenodes or rebalancing processes to get partial namenode's + state, for example partial blocksMap etc.]]> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The tool scans all files and directories, starting from an indicated + root path. The following abnormal conditions are detected and handled:

+
    +
  • files with blocks that are completely missing from all datanodes.
    + In this case the tool can perform one of the following actions: +
      +
    • none ({@link #FIXING_NONE})
    • +
    • move corrupted files to /lost+found directory on DFS + ({@link #FIXING_MOVE}). Remaining data blocks are saved as a + block chains, representing longest consecutive series of valid blocks.
    • +
    • delete corrupted files ({@link #FIXING_DELETE})
    • +
    +
  • +
  • detect files with under-replicated or over-replicated blocks
  • +
+ Additionally, the tool collects a detailed overall DFS statistics, and + optionally can print detailed statistics on block locations and replication + factors of each file.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #syncs}.inc()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #blocksRead}.inc()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        dfs.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        dfs.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically. +

+ Name Node Status info is reported in another MBean + @see org.apache.hadoop.dfs.datanode.metrics.FSDatasetMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data Node runtime statistic info is report in another MBean + @see org.apache.hadoop.dfs.datanode.metrics.DataNodeStatisticsMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name Node runtime statistic info is report in another MBean + @see org.apache.hadoop.dfs.namenode.metrics.NameNodeStatisticsMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        dfs.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        dfs.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically. +

+ Name Node Status info is report in another MBean + @see org.apache.hadoop.dfs.namenode.metrics.FSNamesystemMBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link JobConf}. The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link Mapper} or {@link Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see JobConf + @see JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ']]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is {@link DistributedFileSystem}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +
]]> + + + + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+ + + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ + + + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This method is used to validate the input directories when a job is + submitted so that the {@link JobClient} can fail early, with an useful + error message, in case of errors. For e.g. input directory does not exist. +

+ + @param job job configuration. + @throws InvalidInputException if the job does not have valid input + @deprecated getSplits is called in the client and can perform any + necessary validation of the input]]> +
+
+ + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Note: +

+ @param dir the {@link Path} of the output directory for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + + + + + + + + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see JobTracker#getNewJobId() + @see JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 job job configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/lib/jdiff/hadoop_0.18.2.xml b/lib/jdiff/hadoop_0.18.2.xml new file mode 100644 index 00000000000..08173ab82dc --- /dev/null +++ b/lib/jdiff/hadoop_0.18.2.xml @@ -0,0 +1,38788 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Hadoop by default specifies two resources, loaded in-order from the + classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link JobConf}. The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link Mapper} or {@link Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see JobConf + @see JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ']]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is {@link DistributedFileSystem}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ignore

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> + + + + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+ + + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ + + + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This method is used to validate the input directories when a job is + submitted so that the {@link JobClient} can fail early, with an useful + error message, in case of errors. For e.g. input directory does not exist. +

+ + @param job job configuration. + @throws InvalidInputException if the job does not have valid input + @deprecated getSplits is called in the client and can perform any + necessary validation of the input]]> +
+
+ + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Note: +

+ @param dir the {@link Path} of the output directory for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + + + + + + + + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see JobTracker#getNewJobId() + @see JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 job job configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/lib/jdiff/hadoop_0.18.3.xml b/lib/jdiff/hadoop_0.18.3.xml new file mode 100644 index 00000000000..564916fef77 --- /dev/null +++ b/lib/jdiff/hadoop_0.18.3.xml @@ -0,0 +1,38826 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Hadoop by default specifies two resources, loaded in-order from the + classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link JobConf}. The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link Mapper} or {@link Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see JobConf + @see JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ']]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is {@link DistributedFileSystem}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+ + + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+
+ + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ + + + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This method is used to validate the input directories when a job is + submitted so that the {@link JobClient} can fail early, with an useful + error message, in case of errors. For e.g. input directory does not exist. +

+ + @param job job configuration. + @throws InvalidInputException if the job does not have valid input + @deprecated getSplits is called in the client and can perform any + necessary validation of the input]]> +
+
+ + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Note: +

+ @param dir the {@link Path} of the output directory for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + + + + + + + + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see JobTracker#getNewJobId() + @see JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 job job configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/lib/jdiff/hadoop_0.19.0.xml b/lib/jdiff/hadoop_0.19.0.xml new file mode 100644 index 00000000000..557ac3cc598 --- /dev/null +++ b/lib/jdiff/hadoop_0.19.0.xml @@ -0,0 +1,43972 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param in InputStream to deserialize the object from.]]> + + + + + + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property + as an array of Class. + The value of the property specifies a list of comma separated class names. + If no such property is specified, then defaultValue is + returned. + + @param name the property name. + @param defaultValue default value. + @return property value as a Class[], + or defaultValue.]]> + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + + + + + + + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Unless explicitly turned off, Hadoop by default specifies two + resources, loaded in-order from the classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link org.apache.hadoop.mapred.JobConf}. + The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link org.apache.hadoop.mapred.Mapper}
+     or {@link org.apache.hadoop.mapred.Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see org.apache.hadoop.mapred.JobConf + @see org.apache.hadoop.mapred.JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + @deprecated Consider using {@link GenericOptionsParser} instead.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is DistributedFileSystem.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port. + @param conf Configuration]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair compatible with lzop. + http://www.lzop.org/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FIXME: This array should be in a private or package private location, + since it could be modified by malicious code. +

]]> +
+ + + + This interface is public for historical purposes. You should have no need to + use it. +

]]> +
+ + + + + + + + + + Although BZip2 headers are marked with the magic "Bz" this + constructor expects the next byte in the stream to be the first one after + the magic. Thus callers have to skip the first two bytes. Otherwise this + constructor will throw an exception. +

+ + @throws IOException + if the stream content is malformed or an I/O error occurs. + @throws NullPointerException + if in == null]]> +
+
+ + + + + + + + + + + + + + + The decompression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2InputStream to release the allocated memory. See + {@link CBZip2OutputStream CBZip2OutputStream} for information about memory + usage. +

+ +

+ CBZip2InputStream reads bytes from the compressed source stream via + the single byte {@link java.io.InputStream#read() read()} method exclusively. + Thus you should consider to use a buffered source stream. +

+ +

+ Instances of this class are not threadsafe. +

]]> +
+
+ + + + + + + + CBZip2OutputStream with a blocksize of 900k. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + @param out * + the destination stream. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws NullPointerException + if out == null.]]> +
+
+ + + + CBZip2OutputStream with specified blocksize. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + + @param out + the destination stream. + @param blockSize + the blockSize as 100k units. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws IllegalArgumentException + if (blockSize < 1) || (blockSize > 9). + @throws NullPointerException + if out == null. + + @see #MIN_BLOCKSIZE + @see #MAX_BLOCKSIZE]]> +
+
+ + + + + + + + + + + + + inputLength this method returns MAX_BLOCKSIZE + always. + + @param inputLength + The length of the data which will be compressed by + CBZip2OutputStream.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + == 1.]]> + + + + + == 9.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If you are ever unlucky/improbable enough to get a stack overflow whilst + sorting, increase the following constant and try again. In practice I + have never seen the stack go above 27 elems, so the following limit seems + very generous. +

]]> +
+
+ + + The compression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2OutputStream to release the allocated memory. +

+ +

+ You can shrink the amount of allocated memory and maybe raise the compression + speed by choosing a lower blocksize, which in turn may cause a lower + compression ratio. You can avoid unnecessary memory allocation by avoiding + using a blocksize which is bigger than the size of the input. +

+ +

+ You can compute the memory usage for compressing by the following formula: +

+ +
+ <code>400k + (9 * blocksize)</code>.
+ 
+ +

+ To get the memory required for decompression by {@link CBZip2InputStream + CBZip2InputStream} use +

+ +
+ <code>65k + (5 * blocksize)</code>.
+ 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Memory usage by blocksize
Blocksize Compression
+ memory usage
Decompression
+ memory usage
100k1300k565k
200k2200k1065k
300k3100k1565k
400k4000k2065k
500k4900k2565k
600k5800k3065k
700k6700k3565k
800k7600k4065k
900k8500k4565k
+ +

+ For decompression CBZip2InputStream allocates less memory if the + bzipped input is smaller than one block. +

+ +

+ Instances of this class are not threadsafe. +

+ +

+ TODO: Update to BZip2 1.0.1 +

]]> +
+
+ +
+ + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+
+ + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ +
+ + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Note: The following is valid only if the {@link OutputCommitter} + is {@link FileOutputCommitter}. If OutputCommitter is not + a FileOutputCommitter, the task's temporary output + directory is same as {@link #getOutputPath(JobConf)} i.e. + ${mapred.output.dir}$

+ +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + The generated name can be used to create custom files from within the + different tasks for the job, the names for different tasks will not collide + with each other.

+ +

The given name is postfixed with the task type, 'm' for maps, 'r' for + reduces and the task partition number. For example, give a name 'test' + running on the first map o the job the generated name will be + 'test-m-00000'.

+ + @param conf the configuration for the job. + @param name the name to make unique. + @return a unique name accross all tasks of the job.]]> +
+
+ + + + + The path can be used to create custom files from within the map and + reduce tasks. The path name will be unique for each task. The path parent + will be the job output directory.

ls + +

This method uses the {@link #getUniqueName} method to make the file name + unique for the task.

+ + @param conf the configuration for the job. + @param name the name for the file. + @return a unique path accross all tasks of the job.]]> +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If the parameter {@code loadDefaults} is false, the new instance + will not load resources from the default files. + + @param loadDefaults specifies whether to load from the default files]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + + + + + + + + + + + + + + + + + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see JobTracker#getNewJobId() + @see JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "N/A" + + @return Scheduling information associated to particular Job Queue]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputCommitter describes the commit of task output for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputCommitter of + the job to:

+

    +
  1. + Setup the job during initialization. For example, create the temporary + output directory for the job during the initialization of the job. +
  2. +
  3. + Cleanup the job after the job completion. For example, remove the + temporary output directory after the job completion. +
  4. +
  5. + Setup the task temporary output. +
  6. +
  7. + Check whether a task needs a commit. This is to avoid the commit + procedure if a task does not need commit. +
  8. +
  9. + Commit of the task output. +
  10. +
  11. + Discard the task commit. +
  12. +
+ + @see FileOutputCommitter + @see JobContext + @see TaskAttemptContext]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + Counter of the given group/name.]]> + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + progress of the job's cleanup-tasks, as a float between 0.0 + and 1.0. When all cleanup tasks have completed, the function returns 1.0. + + @return the progress of the job's cleanup-tasks. + @throws IOException]]> + + + + + + progress of the job's setup-tasks, as a float between 0.0 + and 1.0. When all setup tasks have completed, the function returns 1.0. + + @return the progress of the job's setup-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_MAP_PROCESSED_RECORDS}. + false otherwise.]]> + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_REDUCE_PROCESSED_GROUPS}. + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop provides an optional mode of execution in which the bad records + are detected and skipped in further attempts. + +

This feature can be used when map/reduce tasks crashes deterministically on + certain input. This happens due to bugs in the map/reduce function. The usual + course would be to fix these bugs. But sometimes this is not possible; + perhaps the bug is in third party libraries for which the source code is + not available. Due to this, the task never reaches to completion even with + multiple attempts and complete data for that task is lost.

+ +

With this feature, only a small portion of data is lost surrounding + the bad record, which may be acceptable for some user applications. + see {@link SkipBadRecords#setMapperMaxSkipRecords(Configuration, long)}

+ +

The skipping mode gets kicked off after certain no of failures + see {@link SkipBadRecords#setAttemptsToStartSkipping(Configuration, int)}

+ +

In the skipping mode, the map/reduce task maintains the record range which + is getting processed at all times. Before giving the input to the + map/reduce function, it sends this record range to the Task tracker. + If task crashes, the Task tracker knows which one was the last reported + range. On further attempts that range get skipped.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain +

+ + @param job job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + The Mapper classes are invoked in a chained (or piped) fashion, the output of + the first becomes the input of the second, and so on until the last Mapper, + the output of the last Mapper will be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed in a chain. This enables having + reusable specialized Mappers that can be combined to perform composite + operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain. +

+ ChainMapper usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Reducer leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Reducer does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Reducer the configuration given for it, + reducerConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. + + @param job job's JobConf to add the Reducer class. + @param klass the Reducer class to add. + @param inputKeyClass reducer input key class. + @param inputValueClass reducer input value class. + @param outputKeyClass reducer output key class. + @param outputValueClass reducer output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param reducerConf a JobConf with the configuration for the Reducer + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain + . + + @param job chain job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + reduce(...) method of the Reducer with the + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + For each record output by the Reducer, the Mapper classes are invoked in a + chained (or piped) fashion, the output of the first becomes the input of the + second, and so on until the last Mapper, the output of the last Mapper will + be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed after the Reducer or in a chain. + This enables having reusable specialized Mappers that can be combined to + perform composite operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. +

+ ChainReducer usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + @param freq The frequency with which records will be emitted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + This will read every split at the client, which is very expensive. + @param freq Probability with which a key will be chosen. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + Takes the first numSamples / numSplits records from each split. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the name output is multi, false + if it is single. If the name output is not defined it returns + false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + @param conf job conf to enableadd the named output. + @param enabled indicates if the counters will be enabled or not.]]> +
+
+ + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + + @param conf job conf to enableadd the named output. + @return TRUE if the counters are enabled, FALSE if they are disabled.]]> +
+
+ + + + + + + + + + + + + @param namedOutput the named output name + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + + + + + @param namedOutput the named output name + @param multiName the multi name part + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + If overriden subclasses must invoke super.close() at the + end of their close() + + @throws java.io.IOException thrown if any of the MultipleOutput files + could not be closed properly.]]> + + + + OutputCollector passed to + the map() and reduce() methods of the + Mapper and Reducer implementations. +

+ Each additional output, or named output, may be configured with its own + OutputFormat, with its own key class and with its own value + class. +

+ A named output can be a single file or a multi file. The later is refered as + a multi named output. +

+ A multi named output is an unbound set of files all sharing the same + OutputFormat, key class and value class configuration. +

+ When named outputs are used within a Mapper implementation, + key/values written to a name output are not part of the reduce phase, only + key/values written to the job OutputCollector are part of the + reduce phase. +

+ MultipleOutputs supports counters, by default the are disabled. The counters + group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. +

+ Job configuration usage pattern is: +

+
+ JobConf conf = new JobConf();
+
+ conf.setInputPath(inDir);
+ FileOutputFormat.setOutputPath(conf, outDir);
+
+ conf.setMapperClass(MOMap.class);
+ conf.setReducerClass(MOReduce.class);
+ ...
+
+ // Defines additional single text based output 'text' for the job
+ MultipleOutputs.addNamedOutput(conf, "text", TextOutputFormat.class,
+ LongWritable.class, Text.class);
+
+ // Defines additional multi sequencefile based output 'sequence' for the
+ // job
+ MultipleOutputs.addMultiNamedOutput(conf, "seq",
+   SequenceFileOutputFormat.class,
+   LongWritable.class, Text.class);
+ ...
+
+ JobClient jc = new JobClient();
+ RunningJob job = jc.submitJob(conf);
+
+ ...
+ 
+

+ Job configuration usage pattern is: +

+
+ public class MOReduce implements
+   Reducer<WritableComparable, Writable> {
+ private MultipleOutputs mos;
+
+ public void configure(JobConf conf) {
+ ...
+ mos = new MultipleOutputs(conf);
+ }
+
+ public void reduce(WritableComparable key, Iterator<Writable> values,
+ OutputCollector output, Reporter reporter)
+ throws IOException {
+ ...
+ mos.getCollector("text", reporter).collect(key, new Text("Hello"));
+ mos.getCollector("seq", "A", reporter).collect(key, new Text("Bye"));
+ mos.getCollector("seq", "B", reporter).collect(key, new Text("Chau"));
+ ...
+ }
+
+ public void close() throws IOException {
+ mos.close();
+ ...
+ }
+
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + + + + + + + + total.order.partitioner.natural.order is not false, a trie + of the first total.order.partitioner.max.trie.depth(2) + 1 bytes + will be built. Otherwise, keys will be located using a binary search of + the partition keyset using the {@link org.apache.hadoop.io.RawComparator} + defined for this job. The input file must be sorted with the same + comparator and contain {@link + org.apache.hadoop.mapred.JobConf#getNumReduceTasks} - 1 keys.]]> + + + + + + + + + + + + R reduces, there are R-1 + keys in the SequenceFile.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The job can be configured using the static methods in this class, + {@link DBInputFormat}, and {@link DBOutputFormat}. +

+ Alternatively, the properties can be set in the configuration with proper + values. + + @see DBConfiguration#configureDB(JobConf, String, String, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String, String, String...) + @see DBOutputFormat#setOutput(JobConf, String, String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20070101 AND length > 0)' + @param orderBy the fieldNames in the orderBy clause. + @param fieldNames The field names in the table + @see #setInput(JobConf, Class, String, String)]]> + + + + + + + + + + + + + + DBInputFormat emits LongWritables containing the record number as + key and DBWritables as value. + + The SQL query, and input class can be using one of the two + setInput methods.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {@link DBOutputFormat} accepts <key,value> pairs, where + key has a type extending DBWritable. Returned {@link RecordWriter} + writes only the key to the database with a batch SQL query.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DBWritable. DBWritable, is similar to {@link Writable} + except that the {@link #write(PreparedStatement)} method takes a + {@link PreparedStatement}, and {@link #readFields(ResultSet)} + takes a {@link ResultSet}. +

+ Implementations are responsible for writing the fields of the object + to PreparedStatement, and reading the fields of the object from the + ResultSet. + +

Example:

+ If we have the following table in the database : +
+ CREATE TABLE MyTable (
+   counter        INTEGER NOT NULL,
+   timestamp      BIGINT  NOT NULL,
+ );
+ 
+ then we can read/write the tuples from/to the table with : +

+ public class MyWritable implements Writable, DBWritable {
+   // Some data     
+   private int counter;
+   private long timestamp;
+       
+   //Writable#write() implementation
+   public void write(DataOutput out) throws IOException {
+     out.writeInt(counter);
+     out.writeLong(timestamp);
+   }
+       
+   //Writable#readFields() implementation
+   public void readFields(DataInput in) throws IOException {
+     counter = in.readInt();
+     timestamp = in.readLong();
+   }
+       
+   public void write(PreparedStatement statement) throws SQLException {
+     statement.setInt(1, counter);
+     statement.setLong(2, timestamp);
+   }
+       
+   public void readFields(ResultSet resultSet) throws SQLException {
+     counter = resultSet.getInt(1);
+     timestamp = resultSet.getLong(2);
+   } 
+ }
+ 

]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in]]> + + + + + + + out.]]> + + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 conf configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
diff --git a/lib/jdiff/hadoop_0.19.1.xml b/lib/jdiff/hadoop_0.19.1.xml new file mode 100644 index 00000000000..92bdd2c7996 --- /dev/null +++ b/lib/jdiff/hadoop_0.19.1.xml @@ -0,0 +1,44195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param in InputStream to deserialize the object from.]]> + + + + + + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property + as an array of Class. + The value of the property specifies a list of comma separated class names. + If no such property is specified, then defaultValue is + returned. + + @param name the property name. + @param defaultValue default value. + @return property value as a Class[], + or defaultValue.]]> + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + + + + + + + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Unless explicitly turned off, Hadoop by default specifies two + resources, loaded in-order from the classpath:

    +
  1. hadoop-default.xml + : Read-only defaults for hadoop.
  2. +
  3. hadoop-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + hadoop-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link org.apache.hadoop.mapred.JobConf}. + The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link org.apache.hadoop.mapred.Mapper}
+     or {@link org.apache.hadoop.mapred.Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see org.apache.hadoop.mapred.JobConf + @see org.apache.hadoop.mapred.JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + @deprecated Consider using {@link GenericOptionsParser} instead.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is DistributedFileSystem.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port. + @param conf Configuration]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-lzo library is loaded & initialized; + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair. + http://www.oberhumer.com/opensource/lzo/]]> + + + + + + + + + + + + + + + + + + + + + + + lzo compression/decompression pair compatible with lzop. + http://www.lzop.org/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FIXME: This array should be in a private or package private location, + since it could be modified by malicious code. +

]]> +
+ + + + This interface is public for historical purposes. You should have no need to + use it. +

]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Although BZip2 headers are marked with the magic "Bz" this + constructor expects the next byte in the stream to be the first one after + the magic. Thus callers have to skip the first two bytes. Otherwise this + constructor will throw an exception. +

+ + @throws IOException + if the stream content is malformed or an I/O error occurs. + @throws NullPointerException + if in == null]]> +
+
+ + + + + + + + + + + + + + + The decompression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2InputStream to release the allocated memory. See + {@link CBZip2OutputStream CBZip2OutputStream} for information about memory + usage. +

+ +

+ CBZip2InputStream reads bytes from the compressed source stream via + the single byte {@link java.io.InputStream#read() read()} method exclusively. + Thus you should consider to use a buffered source stream. +

+ +

+ Instances of this class are not threadsafe. +

]]> +
+
+ + + + + + + + CBZip2OutputStream with a blocksize of 900k. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + @param out * + the destination stream. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws NullPointerException + if out == null.]]> +
+
+ + + + CBZip2OutputStream with specified blocksize. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + + @param out + the destination stream. + @param blockSize + the blockSize as 100k units. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws IllegalArgumentException + if (blockSize < 1) || (blockSize > 9). + @throws NullPointerException + if out == null. + + @see #MIN_BLOCKSIZE + @see #MAX_BLOCKSIZE]]> +
+
+ + + + + + + + + + + + + inputLength this method returns MAX_BLOCKSIZE + always. + + @param inputLength + The length of the data which will be compressed by + CBZip2OutputStream.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + == 1.]]> + + + + + == 9.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If you are ever unlucky/improbable enough to get a stack overflow whilst + sorting, increase the following constant and try again. In practice I + have never seen the stack go above 27 elems, so the following limit seems + very generous. +

]]> +
+
+ + + The compression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2OutputStream to release the allocated memory. +

+ +

+ You can shrink the amount of allocated memory and maybe raise the compression + speed by choosing a lower blocksize, which in turn may cause a lower + compression ratio. You can avoid unnecessary memory allocation by avoiding + using a blocksize which is bigger than the size of the input. +

+ +

+ You can compute the memory usage for compressing by the following formula: +

+ +
+ <code>400k + (9 * blocksize)</code>.
+ 
+ +

+ To get the memory required for decompression by {@link CBZip2InputStream + CBZip2InputStream} use +

+ +
+ <code>65k + (5 * blocksize)</code>.
+ 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Memory usage by blocksize
Blocksize Compression
+ memory usage
Decompression
+ memory usage
100k1300k565k
200k2200k1065k
300k3100k1565k
400k4000k2065k
500k4900k2565k
600k5800k3065k
700k6700k3565k
800k7600k4065k
900k8500k4565k
+ +

+ For decompression CBZip2InputStream allocates less memory if the + bzipped input is smaller than one block. +

+ +

+ Instances of this class are not threadsafe. +

+ +

+ TODO: Update to BZip2 1.0.1 +

]]> +
+
+ +
+ + + + + + + + + + + + + + + + + true if lzo compressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if lzo decompressors are loaded & initialized, + else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+
+ + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ +
+ + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +

    +
  1. + Size of the cluster. +
  2. +
  3. + Task capacity of the cluster. +
  4. +
  5. + The number of currently running map & reduce tasks. +
  6. +
  7. + State of the JobTracker. +
  8. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Note: The following is valid only if the {@link OutputCommitter} + is {@link FileOutputCommitter}. If OutputCommitter is not + a FileOutputCommitter, the task's temporary output + directory is same as {@link #getOutputPath(JobConf)} i.e. + ${mapred.output.dir}$

+ +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + The generated name can be used to create custom files from within the + different tasks for the job, the names for different tasks will not collide + with each other.

+ +

The given name is postfixed with the task type, 'm' for maps, 'r' for + reduces and the task partition number. For example, give a name 'test' + running on the first map o the job the generated name will be + 'test-m-00000'.

+ + @param conf the configuration for the job. + @param name the name to make unique. + @return a unique name accross all tasks of the job.]]> +
+
+ + + + + The path can be used to create custom files from within the map and + reduce tasks. The path name will be unique for each task. The path parent + will be the job output directory.

ls + +

This method uses the {@link #getUniqueName} method to make the file name + unique for the task.

+ + @param conf the configuration for the job. + @param name the name for the file. + @return a unique path accross all tasks of the job.]]> +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If the parameter {@code loadDefaults} is false, the new instance + will not load resources from the default files. + + @param loadDefaults specifies whether to load from the default files]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is a task-level aggregation operation which, in some cases, + helps to cut down the amount of data transferred from the {@link Mapper} to + the {@link Reducer}, leading to better performance.

+ +

Typically the combiner is same as the the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + + + + + + + + + + + + + + + + + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +
    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see JobTracker#getNewJobId() + @see JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "N/A" + + @return Scheduling information associated to particular Job Queue]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -archives + -files inputjar args]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("mapred.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit]]> +
+
+ + + + + + + + + + + + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputCommitter describes the commit of task output for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputCommitter of + the job to:

+

    +
  1. + Setup the job during initialization. For example, create the temporary + output directory for the job during the initialization of the job. +
  2. +
  3. + Cleanup the job after the job completion. For example, remove the + temporary output directory after the job completion. +
  4. +
  5. + Setup the task temporary output. +
  6. +
  7. + Check whether a task needs a commit. This is to avoid the commit + procedure if a task does not need commit. +
  8. +
  9. + Commit of the task output. +
  10. +
  11. + Discard the task commit. +
  12. +
+ + @see FileOutputCommitter + @see JobContext + @see TaskAttemptContext]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase]]> +
+
+ + + + + + + + + + + + + + + Counter of the given group/name.]]> + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + progress of the job's cleanup-tasks, as a float between 0.0 + and 1.0. When all cleanup tasks have completed, the function returns 1.0. + + @return the progress of the job's cleanup-tasks. + @throws IOException]]> + + + + + + progress of the job's setup-tasks, as a float between 0.0 + and 1.0. When all setup tasks have completed, the function returns 1.0. + + @return the progress of the job's setup-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_MAP_PROCESSED_RECORDS}. + false otherwise.]]> + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_REDUCE_PROCESSED_GROUPS}. + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop provides an optional mode of execution in which the bad records + are detected and skipped in further attempts. + +

This feature can be used when map/reduce tasks crashes deterministically on + certain input. This happens due to bugs in the map/reduce function. The usual + course would be to fix these bugs. But sometimes this is not possible; + perhaps the bug is in third party libraries for which the source code is + not available. Due to this, the task never reaches to completion even with + multiple attempts and complete data for that task is lost.

+ +

With this feature, only a small portion of data is lost surrounding + the bad record, which may be acceptable for some user applications. + see {@link SkipBadRecords#setMapperMaxSkipRecords(Configuration, long)}

+ +

The skipping mode gets kicked off after certain no of failures + see {@link SkipBadRecords#setAttemptsToStartSkipping(Configuration, int)}

+ +

In the skipping mode, the map/reduce task maintains the record range which + is getting processed at all times. Before giving the input to the + map/reduce function, it sends this record range to the Task tracker. + If task crashes, the Task tracker knows which one was the last reported + range. On further attempts that range get skipped.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain +

+ + @param job job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + The Mapper classes are invoked in a chained (or piped) fashion, the output of + the first becomes the input of the second, and so on until the last Mapper, + the output of the last Mapper will be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed in a chain. This enables having + reusable specialized Mappers that can be combined to perform composite + operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain. +

+ ChainMapper usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Reducer leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Reducer does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Reducer the configuration given for it, + reducerConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. + + @param job job's JobConf to add the Reducer class. + @param klass the Reducer class to add. + @param inputKeyClass reducer input key class. + @param inputValueClass reducer input value class. + @param outputKeyClass reducer output key class. + @param outputValueClass reducer output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param reducerConf a JobConf with the configuration for the Reducer + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain + . + + @param job chain job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + reduce(...) method of the Reducer with the + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + For each record output by the Reducer, the Mapper classes are invoked in a + chained (or piped) fashion, the output of the first becomes the input of the + second, and so on until the last Mapper, the output of the last Mapper will + be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed after the Reducer or in a chain. + This enables having reusable specialized Mappers that can be combined to + perform composite operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. +

+ ChainReducer usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + @param freq The frequency with which records will be emitted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + This will read every split at the client, which is very expensive. + @param freq Probability with which a key will be chosen. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + Takes the first numSamples / numSplits records from each split. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the name output is multi, false + if it is single. If the name output is not defined it returns + false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + @param conf job conf to enableadd the named output. + @param enabled indicates if the counters will be enabled or not.]]> +
+
+ + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + + @param conf job conf to enableadd the named output. + @return TRUE if the counters are enabled, FALSE if they are disabled.]]> +
+
+ + + + + + + + + + + + + @param namedOutput the named output name + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + + + + + @param namedOutput the named output name + @param multiName the multi name part + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + If overriden subclasses must invoke super.close() at the + end of their close() + + @throws java.io.IOException thrown if any of the MultipleOutput files + could not be closed properly.]]> + + + + OutputCollector passed to + the map() and reduce() methods of the + Mapper and Reducer implementations. +

+ Each additional output, or named output, may be configured with its own + OutputFormat, with its own key class and with its own value + class. +

+ A named output can be a single file or a multi file. The later is refered as + a multi named output. +

+ A multi named output is an unbound set of files all sharing the same + OutputFormat, key class and value class configuration. +

+ When named outputs are used within a Mapper implementation, + key/values written to a name output are not part of the reduce phase, only + key/values written to the job OutputCollector are part of the + reduce phase. +

+ MultipleOutputs supports counters, by default the are disabled. The counters + group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. +

+ Job configuration usage pattern is: +

+
+ JobConf conf = new JobConf();
+
+ conf.setInputPath(inDir);
+ FileOutputFormat.setOutputPath(conf, outDir);
+
+ conf.setMapperClass(MOMap.class);
+ conf.setReducerClass(MOReduce.class);
+ ...
+
+ // Defines additional single text based output 'text' for the job
+ MultipleOutputs.addNamedOutput(conf, "text", TextOutputFormat.class,
+ LongWritable.class, Text.class);
+
+ // Defines additional multi sequencefile based output 'sequence' for the
+ // job
+ MultipleOutputs.addMultiNamedOutput(conf, "seq",
+   SequenceFileOutputFormat.class,
+   LongWritable.class, Text.class);
+ ...
+
+ JobClient jc = new JobClient();
+ RunningJob job = jc.submitJob(conf);
+
+ ...
+ 
+

+ Job configuration usage pattern is: +

+
+ public class MOReduce implements
+   Reducer<WritableComparable, Writable> {
+ private MultipleOutputs mos;
+
+ public void configure(JobConf conf) {
+ ...
+ mos = new MultipleOutputs(conf);
+ }
+
+ public void reduce(WritableComparable key, Iterator<Writable> values,
+ OutputCollector output, Reporter reporter)
+ throws IOException {
+ ...
+ mos.getCollector("text", reporter).collect(key, new Text("Hello"));
+ mos.getCollector("seq", "A", reporter).collect(key, new Text("Bye"));
+ mos.getCollector("seq", "B", reporter).collect(key, new Text("Chau"));
+ ...
+ }
+
+ public void close() throws IOException {
+ mos.close();
+ ...
+ }
+
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens.]]> + + + + + + + + + + + + total.order.partitioner.natural.order is not false, a trie + of the first total.order.partitioner.max.trie.depth(2) + 1 bytes + will be built. Otherwise, keys will be located using a binary search of + the partition keyset using the {@link org.apache.hadoop.io.RawComparator} + defined for this job. The input file must be sorted with the same + comparator and contain {@link + org.apache.hadoop.mapred.JobConf#getNumReduceTasks} - 1 keys.]]> + + + + + + + + + + + + R reduces, there are R-1 + keys in the SequenceFile.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The job can be configured using the static methods in this class, + {@link DBInputFormat}, and {@link DBOutputFormat}. +

+ Alternatively, the properties can be set in the configuration with proper + values. + + @see DBConfiguration#configureDB(JobConf, String, String, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String, String, String...) + @see DBOutputFormat#setOutput(JobConf, String, String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20070101 AND length > 0)' + @param orderBy the fieldNames in the orderBy clause. + @param fieldNames The field names in the table + @see #setInput(JobConf, Class, String, String)]]> + + + + + + + + + + + + + + DBInputFormat emits LongWritables containing the record number as + key and DBWritables as value. + + The SQL query, and input class can be using one of the two + setInput methods.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {@link DBOutputFormat} accepts <key,value> pairs, where + key has a type extending DBWritable. Returned {@link RecordWriter} + writes only the key to the database with a batch SQL query.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DBWritable. DBWritable, is similar to {@link Writable} + except that the {@link #write(PreparedStatement)} method takes a + {@link PreparedStatement}, and {@link #readFields(ResultSet)} + takes a {@link ResultSet}. +

+ Implementations are responsible for writing the fields of the object + to PreparedStatement, and reading the fields of the object from the + ResultSet. + +

Example:

+ If we have the following table in the database : +
+ CREATE TABLE MyTable (
+   counter        INTEGER NOT NULL,
+   timestamp      BIGINT  NOT NULL,
+ );
+ 
+ then we can read/write the tuples from/to the table with : +

+ public class MyWritable implements Writable, DBWritable {
+   // Some data     
+   private int counter;
+   private long timestamp;
+       
+   //Writable#write() implementation
+   public void write(DataOutput out) throws IOException {
+     out.writeInt(counter);
+     out.writeLong(timestamp);
+   }
+       
+   //Writable#readFields() implementation
+   public void readFields(DataInput in) throws IOException {
+     counter = in.readInt();
+     timestamp = in.readLong();
+   }
+       
+   public void write(PreparedStatement statement) throws SQLException {
+     statement.setInt(1, counter);
+     statement.setLong(2, timestamp);
+   }
+       
+   public void readFields(ResultSet resultSet) throws SQLException {
+     counter = resultSet.getInt(1);
+     timestamp = resultSet.getLong(2);
+   } 
+ }
+ 

]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in]]> + + + + + + + out.]]> + + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 conf configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
diff --git a/lib/jdiff/hadoop_0.20.0.xml b/lib/jdiff/hadoop_0.20.0.xml new file mode 100644 index 00000000000..ce6f91bfe60 --- /dev/null +++ b/lib/jdiff/hadoop_0.20.0.xml @@ -0,0 +1,52140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + final. + + @param name resource to be added, the classpath is examined for a file + with that name.]]> + + + + + + final. + + @param url url of the resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param file file-path of resource to be added, the local filesystem is + examined directly to find the resource, without referring to + the classpath.]]> + + + + + + final. + + @param in InputStream to deserialize the object from.]]> + + + + + + + + + + + name property, null if + no such property exists. + + Values are processed for variable expansion + before being returned. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + name property, without doing + variable expansion. + + @param name the property name. + @return the value of the name property, + or null if no such property exists.]]> + + + + + + + value of the name property. + + @param name property name. + @param value property value.]]> + + + + + + + + + + + + + + name property. If no such property + exists, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value, or defaultValue if the property + doesn't exist.]]> + + + + + + + name property as an int. + + If no such property exists, or if the specified value is not a valid + int, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as an int, + or defaultValue.]]> + + + + + + + name property to an int. + + @param name property name. + @param value int value of the property.]]> + + + + + + + name property as a long. + If no such property is specified, or if the specified value is not a valid + long, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a long, + or defaultValue.]]> + + + + + + + name property to a long. + + @param name property name. + @param value long value of the property.]]> + + + + + + + name property as a float. + If no such property is specified, or if the specified value is not a valid + float, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a float, + or defaultValue.]]> + + + + + + + name property to a float. + + @param name property name. + @param value property value.]]> + + + + + + + name property as a boolean. + If no such property is specified, or if the specified value is not a valid + boolean, then defaultValue is returned. + + @param name property name. + @param defaultValue default value. + @return property value as a boolean, + or defaultValue.]]> + + + + + + + name property to a boolean. + + @param name property name. + @param value boolean value of the property.]]> + + + + + + + + + + + + + + + + + + + + name property as + a collection of Strings. + If no such property is specified then empty collection is returned. +

+ This is an optimized version of {@link #getStrings(String)} + + @param name property name. + @return property value as a collection of Strings.]]> + + + + + + name property as + an array of Strings. + If no such property is specified then null is returned. + + @param name property name. + @return property value as an array of Strings, + or null.]]> + + + + + + + name property as + an array of Strings. + If no such property is specified then default value is returned. + + @param name property name. + @param defaultValue The default value + @return property value as an array of Strings, + or default value.]]> + + + + + + + name property as + as comma delimited values. + + @param name property name. + @param values The values]]> + + + + + + + + + + + + + + name property + as an array of Class. + The value of the property specifies a list of comma separated class names. + If no such property is specified, then defaultValue is + returned. + + @param name the property name. + @param defaultValue default value. + @return property value as a Class[], + or defaultValue.]]> + + + + + + + name property as a Class. + If no such property is specified, then defaultValue is + returned. + + @param name the class name. + @param defaultValue default value. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property as a Class + implementing the interface specified by xface. + + If no such property is specified, then defaultValue is + returned. + + An exception is thrown if the returned class does not implement the named + interface. + + @param name the class name. + @param defaultValue default value. + @param xface the interface implemented by the named class. + @return property value as a Class, + or defaultValue.]]> + + + + + + + + name property to the name of a + theClass implementing the given interface xface. + + An exception is thrown if theClass does not implement the + interface xface. + + @param name property name. + @param theClass property value. + @param xface the interface implemented by the named class.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + dirsProp with + the given path. If dirsProp contains multiple directories, + then one is chosen based on path's hash code. If the selected + directory does not exist, an attempt is made to create it. + + @param dirsProp directory in which to locate the file. + @param path file-path. + @return local file under the directory with the given path.]]> + + + + + + + + + + + + name. + + @param name configuration resource name. + @return an input stream attached to the resource.]]> + + + + + + name. + + @param name configuration resource name. + @return a reader attached to the resource.]]> + + + + + + + + + + + + + + + String + key-value pairs in the configuration. + + @return an iterator over the entries.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + true to set quiet-mode on, false + to turn it off.]]> + + + + + + + + + + + + + + + + + + + Resources + +

Configurations are specified by resources. A resource contains a set of + name/value pairs as XML data. Each resource is named by either a + String or by a {@link Path}. If named by a String, + then the classpath is examined for a file with that name. If named by a + Path, then the local filesystem is examined directly, without + referring to the classpath. + +

Unless explicitly turned off, Hadoop by default specifies two + resources, loaded in-order from the classpath:

    +
  1. core-default.xml + : Read-only defaults for hadoop.
  2. +
  3. core-site.xml: Site-specific configuration for a given hadoop + installation.
  4. +
+ Applications may add additional resources, which are loaded + subsequent to these resources in the order they are added. + +

Final Parameters

+ +

Configuration parameters may be declared final. + Once a resource declares a value final, no subsequently-loaded + resource can alter that value. + For example, one might define a final parameter with: +

+  <property>
+    <name>dfs.client.buffer.dir</name>
+    <value>/tmp/hadoop/dfs/client</value>
+    <final>true</final>
+  </property>
+ + Administrators typically define parameters as final in + core-site.xml for values that user applications may not alter. + +

Variable Expansion

+ +

Value strings are first processed for variable expansion. The + available properties are:

    +
  1. Other properties defined in this Configuration; and, if a name is + undefined here,
  2. +
  3. Properties in {@link System#getProperties()}.
  4. +
+ +

For example, if a configuration resource contains the following property + definitions: +

+  <property>
+    <name>basedir</name>
+    <value>/user/${user.name}</value>
+  </property>
+  
+  <property>
+    <name>tempdir</name>
+    <value>${basedir}/tmp</value>
+  </property>
+ + When conf.get("tempdir") is called, then ${basedir} + will be resolved to another property in this Configuration, while + ${user.name} would then ordinarily be resolved to the value + of the System property with that name.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DistributedCache is a facility provided by the Map-Reduce + framework to cache files (text, archives, jars etc.) needed by applications. +

+ +

Applications specify the files, via urls (hdfs:// or http://) to be cached + via the {@link org.apache.hadoop.mapred.JobConf}. + The DistributedCache assumes that the + files specified via hdfs:// urls are already present on the + {@link FileSystem} at the path specified by the url.

+ +

The framework will copy the necessary files on to the slave node before + any tasks for the job are executed on that node. Its efficiency stems from + the fact that the files are only copied once per job and the ability to + cache archives which are un-archived on the slaves.

+ +

DistributedCache can be used to distribute simple, read-only + data/text files and/or more complex types such as archives, jars etc. + Archives (zip, tar and tgz/tar.gz files) are un-archived at the slave nodes. + Jars may be optionally added to the classpath of the tasks, a rudimentary + software distribution mechanism. Files have execution permissions. + Optionally users can also direct it to symlink the distributed cache file(s) + into the working directory of the task.

+ +

DistributedCache tracks modification timestamps of the cache + files. Clearly the cache files should not be modified by the application + or externally while the job is executing.

+ +

Here is an illustrative example on how to use the + DistributedCache:

+

+     // Setting up the cache for the application
+     
+     1. Copy the requisite files to the FileSystem:
+     
+     $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat  
+     $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip  
+     $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar
+     $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar
+     $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz
+     $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz
+     
+     2. Setup the application's JobConf:
+     
+     JobConf job = new JobConf();
+     DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"), 
+                                   job);
+     DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);
+     DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);
+     DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);
+     
+     3. Use the cached files in the {@link org.apache.hadoop.mapred.Mapper}
+     or {@link org.apache.hadoop.mapred.Reducer}:
+     
+     public static class MapClass extends MapReduceBase  
+     implements Mapper<K, V, K, V> {
+     
+       private Path[] localArchives;
+       private Path[] localFiles;
+       
+       public void configure(JobConf job) {
+         // Get the cached archives/files
+         localArchives = DistributedCache.getLocalCacheArchives(job);
+         localFiles = DistributedCache.getLocalCacheFiles(job);
+       }
+       
+       public void map(K key, V value, 
+                       OutputCollector<K, V> output, Reporter reporter) 
+       throws IOException {
+         // Use data from the cached archives/files here
+         // ...
+         // ...
+         output.collect(k, v);
+       }
+     }
+     
+ 

+ + @see org.apache.hadoop.mapred.JobConf + @see org.apache.hadoop.mapred.JobClient]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BufferedFSInputStream + with the specified buffer size, + and saves its argument, the input stream + in, for later use. An internal + buffer array of length size + is created and stored in buf. + + @param in the underlying input stream. + @param size the buffer size. + @exception IllegalArgumentException if size <= 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setReplication of FileSystem + @param src file name + @param replication new replication + @throws IOException + @return true if successful; + false if file does not exist or is a directory]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fs.scheme.class whose value names the FileSystem class. + The entire URI is passed to the FileSystem instance's initialize method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Return all the files that match filePattern and are not checksum + files. Results are sorted by their names. + +

+ A filename pattern is composed of regular characters and + special pattern matching characters, which are: + +

+
+
+

+

? +
Matches any single character. + +

+

* +
Matches zero or more characters. + +

+

[abc] +
Matches a single character from character set + {a,b,c}. + +

+

[a-b] +
Matches a single character from the character range + {a...b}. Note that character a must be + lexicographically less than or equal to character b. + +

+

[^a] +
Matches a single character that is not from character set or range + {a}. Note that the ^ character must occur + immediately to the right of the opening bracket. + +

+

\c +
Removes (escapes) any special meaning of character c. + +

+

{ab,cd} +
Matches a string from the string set {ab, cd} + +

+

{ab,c{de,fh}} +
Matches a string from the string set {ab, cde, cfh} + +
+
+
+ + @param pathPattern a regular expression specifying a pth pattern + + @return an array of paths that match the path pattern + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All user code that may potentially use the Hadoop Distributed + File System should be written to use a FileSystem object. The + Hadoop DFS is a multi-machine system that appears as a single + disk. It's useful because of its fault tolerance and potentially + very large capacity. + +

+ The local implementation is {@link LocalFileSystem} and distributed + implementation is DistributedFileSystem.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilterFileSystem contains + some other file system, which it uses as + its basic file system, possibly transforming + the data along the way or providing additional + functionality. The class FilterFileSystem + itself simply overrides all methods of + FileSystem with versions that + pass all requests to the contained file + system. Subclasses of FilterFileSystem + may further override some of these methods + and may also provide additional methods + and fields.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buf at offset + and checksum into checksum. + The method is used for implementing read, therefore, it should be optimized + for sequential reading + @param pos chunkPos + @param buf desitination buffer + @param offset offset in buf at which to store data + @param len maximun number of bytes to read + @return number of bytes read]]> + + + + + + + + + + + + + + + + + -1 if the end of the + stream is reached. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + This method implements the general contract of the corresponding + {@link InputStream#read(byte[], int, int) read} method of + the {@link InputStream} class. As an additional + convenience, it attempts to read as many bytes as possible by repeatedly + invoking the read method of the underlying stream. This + iterated read continues until one of the following + conditions becomes true:

    + +
  • The specified number of bytes have been read, + +
  • The read method of the underlying stream returns + -1, indicating end-of-file. + +
If the first read on the underlying stream returns + -1 to indicate end-of-file then this method returns + -1. Otherwise this method returns the number of bytes + actually read. + + @param b destination buffer. + @param off offset at which to start storing bytes. + @param len maximum number of bytes to read. + @return the number of bytes read, or -1 if the end of + the stream has been reached. + @exception IOException if an I/O error occurs. + ChecksumException if any checksum error occurs]]> +
+ + + + + + + + + + + + + + + + + + n bytes of data from the + input stream. + +

This method may skip more bytes than are remaining in the backing + file. This produces no exception and the number of bytes skipped + may include some number of bytes that were beyond the EOF of the + backing file. Attempting to read from the stream after skipping past + the end will result in -1 indicating the end of the file. + +

If n is negative, no bytes are skipped. + + @param n the number of bytes to be skipped. + @return the actual number of bytes skipped. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to skip to is corrupted]]> + + + + + + + This method may seek past the end of the file. + This produces no exception and an attempt to read from + the stream will result in -1 indicating the end of the file. + + @param pos the postion to seek to. + @exception IOException if an I/O error occurs. + ChecksumException if the chunk to seek to is corrupted]]> + + + + + + + + + + len bytes from + stm + + @param stm an input stream + @param buf destiniation buffer + @param offset offset at which to store data + @param len number of bytes to read + @return actual number of bytes read + @throws IOException if there is any IO error]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + len bytes from the specified byte array + starting at offset off and generate a checksum for + each data chunk. + +

This method stores bytes from the given array into this + stream's buffer before it gets checksumed. The buffer gets checksumed + and flushed to the underlying output stream when all data + in a checksum chunk are in the buffer. If the buffer is empty and + requested length is at least as large as the size of next checksum chunk + size, this method will checksum and write the chunk directly + to the underlying output stream. Thus it avoids uneccessary data copy. + + @param b the data. + @param off the start offset in the data. + @param len the number of bytes to write. + @exception IOException if an I/O error occurs.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if and only if pathname + should be included]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trash feature. Files are moved to a user's trash + directory, a subdirectory of their home directory named ".Trash". Files are + initially moved to a current sub-directory of the trash directory. + Within that sub-directory their original path is preserved. Periodically + one may checkpoint the current trash and remove older checkpoints. (This + design permits trash management without enumeration of the full trash + content, without date support in the filesystem, and without clock + synchronization.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} backed by an FTP client provided by Apache Commons Net. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (cause==null ? null : cause.toString()) (which + typically contains the class and detail message of cause). + @param cause the cause (which is saved for later retrieval by the + {@link #getCause()} method). (A null value is + permitted, and indicates that the cause is nonexistent or + unknown.)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is a tool for migrating data from an older to a newer version + of an S3 filesystem. +

+

+ All files in the filesystem are migrated by re-writing the block metadata + - no datafiles are touched. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + Extracts AWS credentials from the filesystem URI or configuration. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A block-based {@link FileSystem} backed by + Amazon S3. +

+ @see NativeS3FileSystem]]> +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If f is a file, this method will make a single call to S3. + If f is a directory, this method will make a maximum of + (n / 1000) + 2 calls to S3, where n is the total number of + files and directories contained directly in f. +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + A {@link FileSystem} for reading and writing files stored on + Amazon S3. + Unlike {@link org.apache.hadoop.fs.s3.S3FileSystem} this implementation + stores files on S3 in their + native form so they can be read by other S3 tools. +

+ @see org.apache.hadoop.fs.s3.S3FileSystem]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + @param name The name of the server + @param port The port to use on the server + @param findPort whether the server should start at the given port and + increment by 1 until it finds a free port. + @param conf Configuration]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points to the log directory + "/static/" -> points to common static files (src/webapps/static) + "/" -> the jsp server code from (src/webapps/)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nth value.]]> + + + + + + + + + + + + + + + + + + + + + nth value in the file.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class IntArrayWritable extends ArrayWritable { + public IntArrayWritable() { + super(IntWritable.class); + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a ByteWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataInputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ DataInputBuffer buffer = new DataInputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using DataInput methods ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new DataOutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ DataOutputBuffer buffer = new DataOutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using DataOutput methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + the class of the item + @param conf the configuration to store + @param item the object to be stored + @param keyName the name of the key to use + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param items the objects to be stored + @param keyName the name of the key to use + @throws IndexOutOfBoundsException if the items array is empty + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + + + + + the class of the item + @param conf the configuration to use + @param keyName the name of the key to use + @param itemClass the class of the item + @return restored object + @throws IOException : forwards Exceptions from the underlying + {@link Serialization} classes.]]> + + + + + DefaultStringifier offers convenience methods to store/load objects to/from + the configuration. + + @param the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a DoubleWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a FloatWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When two sequence files, which have same Key type but different Value + types, are mapped out to reduce, multiple Value types is not allowed. + In this case, this class can help you wrap instances with different types. +

+ +

+ Compared with ObjectWritable, this class is much more effective, + because ObjectWritable will append the class declaration as a String + into the output file in every Key-Value pair. +

+ +

+ Generic Writable implements {@link Configurable} interface, so that it will be + configured by the framework. The configuration is passed to the wrapped objects + implementing {@link Configurable} interface before deserialization. +

+ + how to use it:
+ 1. Write your own class, such as GenericObject, which extends GenericWritable.
+ 2. Implements the abstract method getTypes(), defines + the classes which will be wrapped in GenericObject in application. + Attention: this classes defined in getTypes() method, must + implement Writable interface. +

+ + The code looks like this: +
+ public class GenericObject extends GenericWritable {
+ 
+   private static Class[] CLASSES = {
+               ClassType1.class, 
+               ClassType2.class,
+               ClassType3.class,
+               };
+
+   protected Class[] getTypes() {
+       return CLASSES;
+   }
+
+ }
+ 
+ + @since Nov 8, 2006]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new InputStream and + ByteArrayInputStream each time data is read. + +

Typical usage is something like the following:

+
+ InputBuffer buffer = new InputBuffer();
+ while (... loop condition ...) {
+   byte[] data = ... get data ...;
+   int dataLength = ... get data length ...;
+   buffer.reset(data, dataLength);
+   ... read buffer using InputStream methods ...
+ }
+ 
+ @see DataInputBuffer + @see DataOutput]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a IntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + closes the input and output streams + at the end. + @param in InputStrem to read from + @param out OutputStream to write to + @param conf the Configuration object]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a LongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A map is a directory containing two files, the data file, + containing all keys and values in the map, and a smaller index + file, containing a fraction of the keys. The fraction is determined by + {@link Writer#getIndexInterval()}. + +

The index file is read entirely into memory. Thus key implementations + should try to keep themselves small. + +

Map files are created by adding entries in-order. To maintain a large + database, perform updates by copying the previous version of a database and + merging in a sorted change list, to create a new version of the database in + a new file. Sorting large change lists can be done with {@link + SequenceFile.Sorter}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key and + val. Returns true if such a pair exists and false when at + the end of the map]]> + + + + + + + + + + + + + + + + key or if it does not exist, at the first entry + after the named key. + +- * @param key - key that we're trying to find +- * @param val - data value if key is found +- * @return - the key that was the closest match or null if eof.]]> + + + + + + + + + key does not exist, return + the first entry that falls just before the key. Otherwise, + return the record that sorts just after. + @return - the key that was the closest match or null if eof.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is an MD5Hash whose digest contains the + same values.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This saves memory over creating a new OutputStream and + ByteArrayOutputStream each time data is written. + +

Typical usage is something like the following:

+
+ OutputBuffer buffer = new OutputBuffer();
+ while (... loop condition ...) {
+   buffer.reset();
+   ... write buffer using OutputStream methods ...
+   byte[] data = buffer.getData();
+   int dataLength = buffer.getLength();
+   ... write data to its ultimate destination ...
+ }
+ 
+ @see DataOutputBuffer + @see InputBuffer]]> +
+
+ + + + + + + + + + + + + + + A {@link Comparator} that operates directly on byte representations of + objects. +

+ @param + @see DeserializerComparator]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFiles are flat files consisting of binary key/value + pairs. + +

SequenceFile provides {@link Writer}, {@link Reader} and + {@link Sorter} classes for writing, reading and sorting respectively.

+ + There are three SequenceFile Writers based on the + {@link CompressionType} used to compress key/value pairs: +
    +
  1. + Writer : Uncompressed records. +
  2. +
  3. + RecordCompressWriter : Record-compressed files, only compress + values. +
  4. +
  5. + BlockCompressWriter : Block-compressed files, both keys & + values are collected in 'blocks' + separately and compressed. The size of + the 'block' is configurable. +
+ +

The actual compression algorithm used to compress key and/or values can be + specified by using the appropriate {@link CompressionCodec}.

+ +

The recommended way is to use the static createWriter methods + provided by the SequenceFile to chose the preferred format.

+ +

The {@link Reader} acts as the bridge and can read any of the above + SequenceFile formats.

+ +

SequenceFile Formats

+ +

Essentially there are 3 different formats for SequenceFiles + depending on the CompressionType specified. All of them share a + common header described below. + +

+
    +
  • + version - 3 bytes of magic header SEQ, followed by 1 byte of actual + version number (e.g. SEQ4 or SEQ6) +
  • +
  • + keyClassName -key class +
  • +
  • + valueClassName - value class +
  • +
  • + compression - A boolean which specifies if compression is turned on for + keys/values in this file. +
  • +
  • + blockCompression - A boolean which specifies if block-compression is + turned on for keys/values in this file. +
  • +
  • + compression codec - CompressionCodec class which is used for + compression of keys and/or values (if compression is + enabled). +
  • +
  • + metadata - {@link Metadata} for this file. +
  • +
  • + sync - A sync marker to denote end of the header. +
  • +
+ +
Uncompressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Record-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record +
      +
    • Record length
    • +
    • Key length
    • +
    • Key
    • +
    • Compressed Value
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +
Block-Compressed SequenceFile Format
+
    +
  • + Header +
  • +
  • + Record Block +
      +
    • Compressed key-lengths block-size
    • +
    • Compressed key-lengths block
    • +
    • Compressed keys block-size
    • +
    • Compressed keys block
    • +
    • Compressed value-lengths block-size
    • +
    • Compressed value-lengths block
    • +
    • Compressed values block-size
    • +
    • Compressed values block
    • +
    +
  • +
  • + A sync-marker every few 100 bytes or so. +
  • +
+ +

The compressed blocks of key lengths and value lengths consist of the + actual lengths of individual keys/values encoded in ZeroCompressedInteger + format.

+ + @see CompressionCodec]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key, skipping its + value. True if another entry exists, and false at end of file.]]> + + + + + + + + key and + val. Returns true if such a pair exists and false when at + end of file]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The position passed must be a position returned by {@link + SequenceFile.Writer#getLength()} when writing this file. To seek to an arbitrary + position, use {@link SequenceFile.Reader#sync(long)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SegmentDescriptor + @param segments the list of SegmentDescriptors + @param tmpDir the directory to write temporary files into + @return RawKeyValueIterator + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For best performance, applications should make sure that the {@link + Writable#readFields(DataInput)} implementation of their keys is + very efficient. In particular, it should avoid allocating memory.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This always returns a synchronized position. In other words, + immediately after calling {@link SequenceFile.Reader#seek(long)} with a position + returned by this method, {@link SequenceFile.Reader#next(Writable)} may be called. However + the key may be earlier in the file than key last written when this + method was called (e.g., with block-compression, it may be the first key + in the block that was being written when this method was called).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + key. Returns + true if such a key exists and false when at the end of the set.]]> + + + + + + + key. + Returns key, or null if no match exists.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the objects to stringify]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + position. Note that this + method avoids using the converter or doing String instatiation + @return the Unicode scalar value at position or -1 + if the position is invalid or points to a + trailing byte]]> + + + + + + + + + + what in the backing + buffer, starting as position start. The starting + position is measured in bytes and the return value is in + terms of byte position in the buffer. The backing buffer is + not converted to a string for this operation. + @return byte position of the first occurence of the search + string in the UTF-8 buffer or -1 if not found]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a Text with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException.]]> + + + + + + + + + + + + + + + replace is true, then + malformed input is replaced with the + substitution character, which is U+FFFD. Otherwise the + method throws a MalformedInputException. + @return ByteBuffer: bytes stores at ByteBuffer.array() + and length is ByteBuffer.limit()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In + addition, it provides methods for string traversal without converting the + byte array to a string.

Also includes utilities for + serializing/deserialing a string, coding/decoding a string, checking if a + byte array contains valid UTF8 code, calculating the length of an encoded + string.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a UTF8 with the same contents.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + Also includes utilities for efficiently reading and writing UTF-8. + + @deprecated replaced by Text]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is useful when a class may evolve, so that instances written by the + old version of the class may still be processed by the new version. To + handle this situation, {@link #readFields(DataInput)} + implementations should catch {@link VersionMismatchException}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VIntWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o is a VLongWritable with the same value.]]> + + + + + + + + + + + + + + + + + + + + + + + + out. + + @param out DataOuput to serialize this object into. + @throws IOException]]> + + + + + + + in. + +

For efficiency, implementations should attempt to re-use storage in the + existing object where possible.

+ + @param in DataInput to deseriablize this object from. + @throws IOException]]> +
+ + + Any key or value type in the Hadoop Map-Reduce + framework implements this interface.

+ +

Implementations typically implement a static read(DataInput) + method which constructs a new instance, calls {@link #readFields(DataInput)} + and returns the instance.

+ +

Example:

+

+     public class MyWritable implements Writable {
+       // Some data     
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public static MyWritable read(DataInput in) throws IOException {
+         MyWritable w = new MyWritable();
+         w.readFields(in);
+         return w;
+       }
+     }
+ 

]]> +
+ + + + + + + + WritableComparables can be compared to each other, typically + via Comparators. Any type which is to be used as a + key in the Hadoop Map-Reduce framework should implement this + interface.

+ +

Example:

+

+     public class MyWritableComparable implements WritableComparable {
+       // Some data
+       private int counter;
+       private long timestamp;
+       
+       public void write(DataOutput out) throws IOException {
+         out.writeInt(counter);
+         out.writeLong(timestamp);
+       }
+       
+       public void readFields(DataInput in) throws IOException {
+         counter = in.readInt();
+         timestamp = in.readLong();
+       }
+       
+       public int compareTo(MyWritableComparable w) {
+         int thisValue = this.value;
+         int thatValue = ((IntWritable)o).value;
+         return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
+       }
+     }
+ 

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The default implementation reads the data into two {@link + WritableComparable}s (using {@link + Writable#readFields(DataInput)}, then calls {@link + #compare(WritableComparable,WritableComparable)}.]]> + + + + + + + The default implementation uses the natural ordering, calling {@link + Comparable#compareTo(Object)}.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This base implemenation uses the natural ordering. To define alternate + orderings, override {@link #compare(WritableComparable,WritableComparable)}. + +

One may optimize compare-intensive operations by overriding + {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are + provided to assist in optimized implementations of this method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enum type + @param in DataInput to read from + @param enumType Class type of Enum + @return Enum represented by String read from DataInput + @throws IOException]]> + + + + + + + + + + + + + + + + len number of bytes in input streamin + @param in input stream + @param len number of bytes to skip + @throws IOException when skipped less number of bytes]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CompressionCodec for which to get the + Compressor + @return Compressor for the given + CompressionCodec from the pool or a new one]]> + + + + + + CompressionCodec for which to get the + Decompressor + @return Decompressor for the given + CompressionCodec the pool or a new one]]> + + + + + + Compressor to be returned to the pool]]> + + + + + + Decompressor to be returned to the + pool]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implementations are assumed to be buffered. This permits clients to + reposition the underlying input stream then call {@link #resetState()}, + without having to also synchronize client buffers.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true indicating that more input data is required. + + @param b Input data + @param off Start offset + @param len Length]]> + + + + + true if the input data buffer is empty and + #setInput() should be called in order to provide more input.]]> + + + + + + + + + + + + + true if a preset dictionary is needed for decompression. + @return true if a preset dictionary is needed for decompression]]> + + + + + true if the end of the compressed + data output stream has been reached.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FIXME: This array should be in a private or package private location, + since it could be modified by malicious code. +

]]> +
+ + + + This interface is public for historical purposes. You should have no need to + use it. +

]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Although BZip2 headers are marked with the magic "Bz" this + constructor expects the next byte in the stream to be the first one after + the magic. Thus callers have to skip the first two bytes. Otherwise this + constructor will throw an exception. +

+ + @throws IOException + if the stream content is malformed or an I/O error occurs. + @throws NullPointerException + if in == null]]> +
+
+ + + + + + + + + + + + + + + The decompression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2InputStream to release the allocated memory. See + {@link CBZip2OutputStream CBZip2OutputStream} for information about memory + usage. +

+ +

+ CBZip2InputStream reads bytes from the compressed source stream via + the single byte {@link java.io.InputStream#read() read()} method exclusively. + Thus you should consider to use a buffered source stream. +

+ +

+ Instances of this class are not threadsafe. +

]]> +
+
+ + + + + + + + CBZip2OutputStream with a blocksize of 900k. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + @param out * + the destination stream. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws NullPointerException + if out == null.]]> +
+
+ + + + CBZip2OutputStream with specified blocksize. + +

+ Attention: The caller is resonsible to write the two BZip2 magic + bytes "BZ" to the specified stream prior to calling this + constructor. +

+ + + @param out + the destination stream. + @param blockSize + the blockSize as 100k units. + + @throws IOException + if an I/O error occurs in the specified stream. + @throws IllegalArgumentException + if (blockSize < 1) || (blockSize > 9). + @throws NullPointerException + if out == null. + + @see #MIN_BLOCKSIZE + @see #MAX_BLOCKSIZE]]> +
+
+ + + + + + + + + + + + + inputLength this method returns MAX_BLOCKSIZE + always. + + @param inputLength + The length of the data which will be compressed by + CBZip2OutputStream.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + == 1.]]> + + + + + == 9.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If you are ever unlucky/improbable enough to get a stack overflow whilst + sorting, increase the following constant and try again. In practice I + have never seen the stack go above 27 elems, so the following limit seems + very generous. +

]]> +
+
+ + + The compression requires large amounts of memory. Thus you should call the + {@link #close() close()} method as soon as possible, to force + CBZip2OutputStream to release the allocated memory. +

+ +

+ You can shrink the amount of allocated memory and maybe raise the compression + speed by choosing a lower blocksize, which in turn may cause a lower + compression ratio. You can avoid unnecessary memory allocation by avoiding + using a blocksize which is bigger than the size of the input. +

+ +

+ You can compute the memory usage for compressing by the following formula: +

+ +
+ <code>400k + (9 * blocksize)</code>.
+ 
+ +

+ To get the memory required for decompression by {@link CBZip2InputStream + CBZip2InputStream} use +

+ +
+ <code>65k + (5 * blocksize)</code>.
+ 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Memory usage by blocksize
Blocksize Compression
+ memory usage
Decompression
+ memory usage
100k1300k565k
200k2200k1065k
300k3100k1565k
400k4000k2065k
500k4900k2565k
600k5800k3065k
700k6700k3565k
800k7600k4065k
900k8500k4565k
+ +

+ For decompression CBZip2InputStream allocates less memory if the + bzipped input is smaller than one block. +

+ +

+ Instances of this class are not threadsafe. +

+ +

+ TODO: Update to BZip2 1.0.1 +

]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @return the total (non-negative) number of uncompressed bytes input so far]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-zlib is loaded & initialized + and can be loaded for this job, else false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keep trying a limited number of times, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying for a maximum time, waiting a fixed time between attempts, + and then fail by re-throwing the exception. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by the number of tries so far. +

]]> +
+
+ + + + + + + Keep trying a limited number of times, waiting a growing amount of time between attempts, + and then fail by re-throwing the exception. + The time between attempts is sleepTime mutliplied by a random + number in the range of [0, 2 to the number of retries) +

]]> +
+
+ + + + + + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + + + A retry policy for RemoteException + Set a default policy with some explicit handlers for specific exceptions. +

]]> +
+
+ + + + Try once, and fail by re-throwing the exception. + This corresponds to having no retry mechanism in place. +

]]> +
+
+ + + + Try once, and fail silently for void methods, or by + re-throwing the exception for non-void methods. +

]]> +
+
+ + + + Keep trying forever. +

]]> +
+
+ + + A collection of useful implementations of {@link RetryPolicy}. +

]]> +
+
+ + + + + + + + + + Determines whether the framework should retry a + method for the given exception, and the number + of retries that have been made for that operation + so far. +

+ @param e The exception that caused the method to fail. + @param retries The number of times the method has been retried. + @return true if the method should be retried, + false if the method should not be retried + but shouldn't fail with an exception (only for void methods). + @throws Exception The re-thrown exception e indicating + that the method failed and should not be retried further.]]> +
+
+ + + Specifies a policy for retrying method failures. + Implementations of this interface should be immutable. +

]]> +
+
+ + + + + + + + + + + + Create a proxy for an interface of an implementation class + using the same retry policy for each method in the interface. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param retryPolicy the policy for retirying method call failures + @return the retry proxy]]> +
+
+ + + + + + + Create a proxy for an interface of an implementation class + using the a set of retry policies specified by method name. + If no retry policy is defined for a method then a default of + {@link RetryPolicies#TRY_ONCE_THEN_FAIL} is used. +

+ @param iface the interface that the retry will implement + @param implementation the instance whose methods should be retried + @param methodNameToPolicyMap a map of method names to retry policies + @return the retry proxy]]> +
+
+ + + A factory for creating retry proxies. +

]]> +
+
+ +
+ + + + + + + + Prepare the deserializer for reading.

]]> +
+
+ + + + + + Deserialize the next object from the underlying input stream. + If the object t is non-null then this deserializer + may set its internal state to the next object read from the input + stream. Otherwise, if the object t is null a new + deserialized object will be created. +

+ @return the deserialized object]]> +
+
+ + + + Close the underlying input stream and clear up any resources.

]]> +
+
+ + + Provides a facility for deserializing objects of type from an + {@link InputStream}. +

+ +

+ Deserializers are stateful, but must not buffer the input since + other producers may read from the input between calls to + {@link #deserialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + A {@link RawComparator} that uses a {@link Deserializer} to deserialize + the objects to be compared so that the standard {@link Comparator} can + be used to compare them. +

+

+ One may optimize compare-intensive operations by using a custom + implementation of {@link RawComparator} that operates directly + on byte representations. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + An experimental {@link Serialization} for Java {@link Serializable} classes. +

+ @see JavaSerializationComparator]]> +
+
+ + + + + + + + + + + + + A {@link RawComparator} that uses a {@link JavaSerialization} + {@link Deserializer} to deserialize objects that are then compared via + their {@link Comparable} interfaces. +

+ @param + @see JavaSerialization]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + Encapsulates a {@link Serializer}/{@link Deserializer} pair. +

+ @param ]]> +
+
+ + + + + + + Serializations are found by reading the io.serializations + property from conf, which is a comma-delimited list of + classnames. +

]]> +
+
+ + + + + + + + + + + + A factory for {@link Serialization}s. +

]]> +
+
+ + + + + + + + Prepare the serializer for writing.

]]> +
+
+ + + + + Serialize t to the underlying output stream.

]]> +
+
+ + + + Close the underlying output stream and clear up any resources.

]]> +
+
+ + + Provides a facility for serializing objects of type to an + {@link OutputStream}. +

+ +

+ Serializers are stateful, but must not buffer the output since + other producers may write to the output between calls to + {@link #serialize(Object)}. +

+ @param ]]> +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param, to the IPC server running at + address, returning the value. Throws exceptions if there are + network problems or if the remote code threw an exception. + @deprecated Use {@link #call(Writable, InetSocketAddress, Class, UserGroupInformation)} instead]]> + + + + + + + + + + param, to the IPC server running at + address with the ticket credentials, returning + the value. + Throws exceptions if there are network problems or if the remote code + threw an exception. + @deprecated Use {@link #call(Writable, InetSocketAddress, Class, UserGroupInformation)} instead]]> + + + + + + + + + + + param, to the IPC server running at + address which is servicing the protocol protocol, + with the ticket credentials, returning the value. + Throws exceptions if there are network problems or if the remote code + threw an exception.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unwraps any IOException. + + @param lookupTypes the desired exception class. + @return IOException, which is either the lookupClass exception or this.]]> + + + + + This unwraps any Throwable that has a constructor taking + a String as a parameter. + Otherwise it returns this. + + @return Throwable]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + protocol is a Java interface. All parameters and return types must + be one of: + +
  • a primitive type, boolean, byte, + char, short, int, long, + float, double, or void; or
  • + +
  • a {@link String}; or
  • + +
  • a {@link Writable}; or
  • + +
  • an array of the above types
+ + All methods in the protocol should throw only IOException. No field data of + the protocol instance is transmitted.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + handlerCount determines + the number of handler threads that will be used to process calls.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + ,name=RpcActivityForPort" + + Many of the activity metrics are sampled and averaged on an interval + which can be specified in the metrics config file. +

+ For the metrics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most metrics contexts do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically + + + + Impl details: We use a dynamic mbean that gets the list of the metrics + from the metrics registry passed as an argument to the constructor]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class has a number of metrics variables that are publicly accessible; + these variables (objects) have methods to update their values; + for example: +

{@link #rpcQueueTime}.inc(time)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For the statistics that are sampled and averaged, one must specify + a metrics context that does periodic update calls. Most do. + The default Null metrics context however does NOT. So if you aren't + using any other metrics context then you can turn on the viewing and averaging + of sampled metrics by specifying the following two lines + in the hadoop-meterics.properties file: +

+        rpc.class=org.apache.hadoop.metrics.spi.NullContextWithUpdateThread
+        rpc.period=10
+  
+

+ Note that the metrics are collected regardless of the context used. + The context with the update thread is used to average the data periodically]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When constructing the instance, if the factory property + contextName.class exists, + its value is taken to be the name of the class to instantiate. Otherwise, + the default is to create an instance of + org.apache.hadoop.metrics.spi.NullContext, which is a + dummy "no-op" context which will cause all metric data to be discarded. + + @param contextName the name of the context + @return the named MetricsContext]]> + + + + + + + + + + + + + + + + + + + + + When the instance is constructed, this method checks if the file + hadoop-metrics.properties exists on the class path. If it + exists, it must be in the format defined by java.util.Properties, and all + the properties in the file are set as attributes on the newly created + ContextFactory instance. + + @return the singleton ContextFactory instance]]> + + + + getFactory() method.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + startMonitoring() again after calling + this. + @see #close()]]> + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A record name identifies the kind of data to be reported. For example, a + program reporting statistics relating to the disks on a computer might use + a record name "diskStats".

+ + A record has zero or more tags. A tag has a name and a value. To + continue the example, the "diskStats" record might use a tag named + "diskName" to identify a particular disk. Sometimes it is useful to have + more than one tag, so there might also be a "diskType" with value "ide" or + "scsi" or whatever.

+ + A record also has zero or more metrics. These are the named + values that are to be reported to the metrics system. In the "diskStats" + example, possible metric names would be "diskPercentFull", "diskPercentBusy", + "kbReadPerSecond", etc.

+ + The general procedure for using a MetricsRecord is to fill in its tag and + metric values, and then call update() to pass the record to the + client library. + Metric data is not immediately sent to the metrics system + each time that update() is called. + An internal table is maintained, identified by the record name. This + table has columns + corresponding to the tag and the metric names, and rows + corresponding to each unique set of tag values. An update + either modifies an existing row in the table, or adds a new row with a set of + tag values that are different from all the other rows. Note that if there + are no tags, then there can be at most one row in the table.

+ + Once a row is added to the table, its data will be sent to the metrics system + on every timer period, whether or not it has been updated since the previous + timer period. If this is inappropriate, for example if metrics were being + reported by some transient object in an application, the remove() + method can be used to remove the row and thus stop the data from being + sent.

+ + Note that the update() method is atomic. This means that it is + safe for different threads to be updating the same metric. More precisely, + it is OK for different threads to call update() on MetricsRecord instances + with the same set of tag names and tag values. Different threads should + not use the same MetricsRecord instance at the same time.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MetricsContext.registerUpdater().]]> + + + + + + + + + + + + + + + + + + + + + + + + + fileName attribute, + if specified. Otherwise the data will be written to standard + output.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class is configured by setting ContextFactory attributes which in turn + are usually configured through a properties file. All the attributes are + prefixed by the contextName. For example, the properties file might contain: +

+ myContextName.fileName=/tmp/metrics.log
+ myContextName.period=5
+ 
]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contextName.tableName. The returned map consists of + those attributes with the contextName and tableName stripped off.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + recordName. + Throws an exception if the metrics implementation is configured with a fixed + set of record names and recordName is not in that set. + + @param recordName the name of the record + @throws MetricsException if recordName conflicts with configuration data]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This class implements the internal table of metric data, and the timer + on which data is to be sent to the metrics system. Subclasses must + override the abstract emitRecord method in order to transmit + the data.

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + and remove().]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hostname or hostname:port. If + the specs string is null, defaults to localhost:defaultPort. + + @return a list of InetSocketAddress objects.]]> + + + + + + + + + + + + + + + + + + + ,name=" + Where the and are the supplied parameters + + @param serviceName + @param nameName + @param theMbean - the MBean to register + @return the named used to register the MBean]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.rpc.socket.factory.class.<ClassName>. When no + such parameter exists then fall back on the default socket factory as + configured by hadoop.rpc.socket.factory.class.default. If + this default socket factory is not configured, then fall back on the JVM + default socket factory. + + @param conf the configuration + @param clazz the class (usually a {@link VersionedProtocol}) + @return a socket factory]]> + + + + + + hadoop.rpc.socket.factory.default + + @param conf the configuration + @return the default socket factory as specified in the configuration or + the JVM default socket factory if the configuration does not + contain a default socket factory property.]]> + + + + + + + + + + + + + : + ://:/]]> + + + + + + + + : + ://:/]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + From documentation for {@link #getInputStream(Socket, long)}:
+ Returns InputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketInputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getInputStream()} is returned. In the later + case, the timeout argument is ignored and the timeout set with + {@link Socket#setSoTimeout(int)} applies for reads.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see #getInputStream(Socket, long) + + @param socket + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getInputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return InputStream for reading from the socket. + @throws IOException]]> +
+
+ + + + +
+ + From documentation for {@link #getOutputStream(Socket, long)} :
+ Returns OutputStream for the socket. If the socket has an associated + SocketChannel then it returns a + {@link SocketOutputStream} with the given timeout. If the socket does not + have a channel, {@link Socket#getOutputStream()} is returned. In the later + case, the timeout argument is ignored and the write will wait until + data is available.

+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see #getOutputStream(Socket, long) + + @param socket + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + +
+ + Any socket created using socket factories returned by {@link #NetUtils}, + must use this interface instead of {@link Socket#getOutputStream()}. + + @see Socket#getChannel() + + @param socket + @param timeout timeout in milliseconds. This may not always apply. zero + for waiting as long as necessary. + @return OutputStream for writing to the socket. + @throws IOException]]> +
+
+ + + + + + + socket.connect(endpoint, timeout). If + socket.getChannel() returns a non-null channel, + connect is implemented using Hadoop's selectors. This is done mainly + to avoid Sun's connect implementation from creating thread-local + selectors, since Hadoop does not have control on when these are closed + and could end up taking all the available file descriptors. + + @see java.net.Socket#connect(java.net.SocketAddress, int) + + @param socket + @param endpoint + @param timeout - timeout in milliseconds]]> + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + node + + @param node + a node + @return true if node is already in the tree; false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + scope + if scope starts with ~, choose one from the all nodes except for the + ones in scope; otherwise, choose one from scope + @param scope range of nodes from which a node will be choosen + @return the choosen node]]> + + + + + + + scope but not in excludedNodes + if scope starts with ~, return the number of nodes that are not + in scope and excludedNodes; + @param scope a path string that may start with ~ + @param excludedNodes a list of nodes + @return number of available nodes]]> + + + + + + + + + + + + reader + It linearly scans the array, if a local node is found, swap it with + the first element of the array. + If a local rack node is found, swap it with the first element following + the local node. + If neither local node or local rack node is found, put a random replica + location at postion 0. + It leaves the rest nodes untouched.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + +
+ + Create a new input stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + @see SocketInputStream#SocketInputStream(ReadableByteChannel, long) + + @param socket should have a channel associated with it. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + Create a new ouput stream with the given timeout. If the timeout + is zero, it will be treated as infinite timeout. The socket's + channel will be configured to be non-blocking. + + @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) + + @param socket should have a channel associated with it. + @param timeout timeout timeout in milliseconds. must not be negative. + @throws IOException]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = getCount(). + @param newCapacity The new capacity in bytes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index idx = startVector(...); + while (!idx.done()) { + .... // read element of a vector + idx.incr(); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This task takes the given record definition files and compiles them into + java or c++ + files. It is then up to the user to compile the generated files. + +

The task requires the file or the nested fileset element to be + specified. Optional attributes are language (set the output + language, default is "java"), + destdir (name of the destination directory for generated java/c++ + code, default is ".") and failonerror (specifies error handling + behavior. default is true). +

Usage

+
+ <recordcc
+       destdir="${basedir}/gensrc"
+       language="java">
+   <fileset include="**\/*.jr" />
+ </recordcc>
+ 
]]> +
+
+ +
+ + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (cause==null ? null : cause.toString()) (which + typically contains the class and detail message of cause). + @param cause the cause (which is saved for later retrieval by the + {@link #getCause()} method). (A null value is + permitted, and indicates that the cause is nonexistent or + unknown.)]]> + + + + + + + + + + + + + Group with the given groupname. + @param group group name]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi. + @param ugi user + @return the {@link Subject} for the user identified by ugi]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ugi as a comma separated string in + conf as a property attr + + The String starts with the user name followed by the default group names, + and other group names. + + @param conf configuration + @param attr property name + @param ugi a UnixUserGroupInformation]]> + + + + + + + + conf + + The object is expected to store with the property name attr + as a comma separated string that starts + with the user name followed by group names. + If the property name is not defined, return null. + It's assumed that there is only one UGI per user. If this user already + has a UGI in the ugi map, return the ugi in the map. + Otherwise, construct a UGI from the configuration, store it in the + ugi map and return it. + + @param conf configuration + @param attr property name + @return a UnixUGI + @throws LoginException if the stored string is ill-formatted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + User with the given username. + @param user user name]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (cause==null ? null : cause.toString()) (which + typically contains the class and detail message of cause). + @param cause the cause (which is saved for later retrieval by the + {@link #getCause()} method). (A null value is + permitted, and indicates that the cause is nonexistent or + unknown.)]]> + + + + + + + + + + + + + + does not provide the stack trace for security purposes.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + service as related to + Service Level Authorization for Hadoop. + + Each service defines it's configuration key and also the necessary + {@link Permission} required to access the service.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in]]> + + + + + + + out.]]> + + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + reset is true, then resets the checksum. + @return number of bytes written. Will be equal to getChecksumSize();]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenericOptionsParser to parse only the generic Hadoop + arguments. + + The array of string arguments other than the generic arguments can be + obtained by {@link #getRemainingArgs()}. + + @param conf the Configuration to modify. + @param args command-line arguments.]]> + + + + + GenericOptionsParser to parse given options as well + as generic Hadoop options. + + The resulting CommandLine object can be obtained by + {@link #getCommandLine()}. + + @param conf the configuration to modify + @param options options built by the caller + @param args User-specified arguments]]> + + + + + Strings containing the un-parsed arguments + or empty array if commandLine was not defined.]]> + + + + + + + + + + CommandLine object + to process the parsed arguments. + + Note: If the object is created with + {@link #GenericOptionsParser(Configuration, String[])}, then returned + object will only contain parsed generic options. + + @return CommandLine representing list of arguments + parsed against Options descriptor.]]> + + + + + + + + + + + + + + + + + GenericOptionsParser is a utility to parse command line + arguments generic to the Hadoop framework. + + GenericOptionsParser recognizes several standarad command + line arguments, enabling applications to easily specify a namenode, a + jobtracker, additional configuration resources etc. + +

Generic Options

+ +

The supported generic options are:

+

+     -conf <configuration file>     specify a configuration file
+     -D <property=value>            use value for given property
+     -fs <local|namenode:port>      specify a namenode
+     -jt <local|jobtracker:port>    specify a job tracker
+     -files <comma separated list of files>    specify comma separated
+                            files to be copied to the map reduce cluster
+     -libjars <comma separated list of jars>   specify comma separated
+                            jar files to include in the classpath.
+     -archives <comma separated list of archives>    specify comma
+             separated archives to be unarchived on the compute machines.
+
+ 

+ +

The general command line syntax is:

+

+ bin/hadoop command [genericOptions] [commandOptions]
+ 

+ +

Generic command line arguments might modify + Configuration objects, given to constructors.

+ +

The functionality is implemented using Commons CLI.

+ +

Examples:

+

+ $ bin/hadoop dfs -fs darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+ 
+ $ bin/hadoop dfs -D fs.default.name=darwin:8020 -ls /data
+ list /data directory in dfs with namenode darwin:8020
+     
+ $ bin/hadoop dfs -conf hadoop-site.xml -ls /data
+ list /data directory in dfs with conf specified in hadoop-site.xml
+     
+ $ bin/hadoop job -D mapred.job.tracker=darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt darwin:50020 -submit job.xml
+ submit a job to job tracker darwin:50020
+     
+ $ bin/hadoop job -jt local -submit job.xml
+ submit a job to local runner
+ 
+ $ bin/hadoop jar -libjars testlib.jar 
+ -archives test.tgz -files file.txt inputjar args
+ job submission with libjars, files and archives
+ 

+ + @see Tool + @see ToolRunner]]> +
+
+ + + + + + + + + Class<T>) of the + argument of type T. + @param The type of the argument + @param t the object to get it class + @return Class<T>]]> + + + + + + + List<T> to a an array of + T[]. + @param c the Class object of the items in the list + @param list the list to convert]]> + + + + + + List<T> to a an array of + T[]. + @param list the list to convert + @throws ArrayIndexOutOfBoundsException if the list is empty. + Use {@link #toArray(Class, List)} if the list may be empty.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.file.buffer.size specified in the given + Configuration. + @param in input stream + @param conf configuration + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if native-hadoop is loaded, + else false]]> + + + + + + true if native hadoop libraries, if present, can be + used for this job; false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { pq.top().change(); pq.adjustTop(); } + instead of
+  { o = pq.pop(); o.change(); pq.push(o); }
+ 
]]> +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients and/or applications can use the provided Progressable + to explicitly report progress to the Hadoop framework. This is especially + important for operations which take an insignificant amount of time since, + in-lieu of the reported progress, the framework has to assume that an error + has occured and time-out the operation.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class is to be obtained + @return the correctly typed Class of the given object.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop Pipes + or 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 conf configuration + @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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell interface. + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + Shell interface. + @param env the map of environment key=value + @param cmd shell command to execute. + @return the output of the executed command.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell can be used to run unix commands like du or + df. It also offers facilities to gate commands by + time-intervals.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ArrayList of string values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the char to be escaped + @return an escaped string]]> + + + + + + + + + + + + + + + + + + + + + + charToEscape in the string + with the escape char escapeChar + + @param str string + @param escapeChar escape char + @param charToEscape the escaped char + @return an unescaped string]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tool, is the standard for any Map-Reduce tool/application. + The tool/application should delegate the handling of + + standard command-line options to {@link ToolRunner#run(Tool, String[])} + and only handle its custom arguments.

+ +

Here is how a typical Tool is implemented:

+

+     public class MyApp extends Configured implements Tool {
+     
+       public int run(String[] args) throws Exception {
+         // Configuration processed by ToolRunner
+         Configuration conf = getConf();
+         
+         // Create a JobConf using the processed conf
+         JobConf job = new JobConf(conf, MyApp.class);
+         
+         // Process custom command-line options
+         Path in = new Path(args[1]);
+         Path out = new Path(args[2]);
+         
+         // Specify various job-specific parameters     
+         job.setJobName("my-app");
+         job.setInputPath(in);
+         job.setOutputPath(out);
+         job.setMapperClass(MyApp.MyMapper.class);
+         job.setReducerClass(MyApp.MyReducer.class);
+
+         // Submit the job, then poll for progress until the job is complete
+         JobClient.runJob(job);
+       }
+       
+       public static void main(String[] args) throws Exception {
+         // Let ToolRunner handle generic command-line options 
+         int res = ToolRunner.run(new Configuration(), new Sort(), args);
+         
+         System.exit(res);
+       }
+     }
+ 

+ + @see GenericOptionsParser + @see ToolRunner]]> +
+
+ + + + + + + + + + + + Tool by {@link Tool#run(String[])}, after + parsing with the given generic arguments. Uses the given + Configuration, or builds one if null. + + Sets the Tool's configuration with the possibly modified + version of the conf. + + @param conf Configuration for the Tool. + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + Tool with its Configuration. + + Equivalent to run(tool.getConf(), tool, args). + + @param tool Tool to run. + @param args command-line arguments to the tool. + @return exit code of the {@link Tool#run(String[])} method.]]> + + + + + + + + + + ToolRunner can be used to run classes implementing + Tool interface. It works in conjunction with + {@link GenericOptionsParser} to parse the + + generic hadoop command line arguments and modifies the + Configuration of the Tool. The + application-specific options are passed along without being modified. +

+ + @see Tool + @see GenericOptionsParser]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + this filter. + @param nbHash The number of hash function to consider. + @param hashType type of the hashing function (see + {@link org.apache.hadoop.util.hash.Hash}).]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bloom filter, as defined by Bloom in 1970. +

+ The Bloom filter is a data structure that was introduced in 1970 and that has been adopted by + the networking research community in the past decade thanks to the bandwidth efficiencies that it + offers for the transmission of set membership information between networked hosts. A sender encodes + the information into a bit vector, the Bloom filter, that is more compact than a conventional + representation. Computation and space costs for construction are linear in the number of elements. + The receiver uses the filter to test whether various elements are members of the set. Though the + filter will occasionally return a false positive, it will never return a false negative. When creating + the filter, the sender can choose its desired point in a trade-off between the false positive rate and the size. + +

+ Originally created by + European Commission One-Lab Project 034819. + + @see Filter The general behavior of a filter + + @see Space/Time Trade-Offs in Hash Coding with Allowable Errors]]> + + + + + + + + + + + + + this filter. + @param nbHash The number of hash function to consider. + @param hashType type of the hashing function (see + {@link org.apache.hadoop.util.hash.Hash}).]]> + + + + + + + + + this counting Bloom filter. +

+ Invariant: nothing happens if the specified key does not belong to this counter Bloom filter. + @param key The key to remove.]]> + + + + + + + + + + + + key -> count map. +

NOTE: due to the bucket size of this filter, inserting the same + key more than 15 times will cause an overflow at all filter positions + associated with this key, and it will significantly increase the error + rate for this and other keys. For this reason the filter can only be + used to store small count values 0 <= N << 15. + @param key key to be tested + @return 0 if the key is not present. Otherwise, a positive value v will + be returned such that v == count with probability equal to the + error rate of this filter, and v > count otherwise. + Additionally, if the filter experienced an underflow as a result of + {@link #delete(Key)} operation, the return value may be lower than the + count with the probability of the false negative rate of such + filter.]]> + + + + + + + + + + + + + + + + + + + + + + counting Bloom filter, as defined by Fan et al. in a ToN + 2000 paper. +

+ A counting Bloom filter is an improvement to standard a Bloom filter as it + allows dynamic additions and deletions of set membership information. This + is achieved through the use of a counting vector instead of a bit vector. +

+ Originally created by + European Commission One-Lab Project 034819. + + @see Filter The general behavior of a filter + + @see Summary cache: a scalable wide-area web cache sharing protocol]]> + + + + + + + + + + + + + + Builds an empty Dynamic Bloom filter. + @param vectorSize The number of bits in the vector. + @param nbHash The number of hash function to consider. + @param hashType type of the hashing function (see + {@link org.apache.hadoop.util.hash.Hash}). + @param nr The threshold for the maximum number of keys to record in a + dynamic Bloom filter row.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dynamic Bloom filter, as defined in the INFOCOM 2006 paper. +

+ A dynamic Bloom filter (DBF) makes use of a s * m bit matrix but + each of the s rows is a standard Bloom filter. The creation + process of a DBF is iterative. At the start, the DBF is a 1 * m + bit matrix, i.e., it is composed of a single standard Bloom filter. + It assumes that nr elements are recorded in the + initial bit vector, where nr <= n (n is + the cardinality of the set A to record in the filter). +

+ As the size of A grows during the execution of the application, + several keys must be inserted in the DBF. When inserting a key into the DBF, + one must first get an active Bloom filter in the matrix. A Bloom filter is + active when the number of recorded keys, nr, is + strictly less than the current cardinality of A, n. + If an active Bloom filter is found, the key is inserted and + nr is incremented by one. On the other hand, if there + is no active Bloom filter, a new one is created (i.e., a new row is added to + the matrix) according to the current size of A and the element + is added in this new Bloom filter and the nr value of + this new Bloom filter is set to one. A given key is said to belong to the + DBF if the k positions are set to one in one of the matrix rows. +

+ Originally created by + European Commission One-Lab Project 034819. + + @see Filter The general behavior of a filter + @see BloomFilter A Bloom filter + + @see Theory and Network Applications of Dynamic Bloom Filters]]> + + + + + + + + + + + this filter. + @param nbHash The number of hash functions to consider. + @param hashType type of the hashing function (see {@link Hash}).]]> + + + + + + this filter. + @param key The key to add.]]> + + + + + + this filter. + @param key The key to test. + @return boolean True if the specified key belongs to this filter. + False otherwise.]]> + + + + + + this filter and a specified filter. +

+ Invariant: The result is assigned to this filter. + @param filter The filter to AND with.]]> + + + + + + this filter and a specified filter. +

+ Invariant: The result is assigned to this filter. + @param filter The filter to OR with.]]> + + + + + + this filter and a specified filter. +

+ Invariant: The result is assigned to this filter. + @param filter The filter to XOR with.]]> + + + + + this filter. +

+ The result is assigned to this filter.]]> + + + + + + this filter. + @param keys The list of keys.]]> + + + + + + this filter. + @param keys The collection of keys.]]> + + + + + + this filter. + @param keys The array of keys.]]> + + + + + + + + + + + + + this filter.]]> + + + + + + + + + + + + + + + + + + + + A filter is a data structure which aims at offering a lossy summary of a set A. The + key idea is to map entries of A (also called keys) into several positions + in a vector through the use of several hash functions. +

+ Typically, a filter will be implemented as a Bloom filter (or a Bloom filter extension). +

+ It must be extended in order to define the real behavior. + + @see Key The general behavior of a key + @see HashFunction A hash function]]> + + + + + + + + + Builds a hash function that must obey to a given maximum number of returned values and a highest value. + @param maxValue The maximum highest returned value. + @param nbHash The number of resulting hashed values. + @param hashType type of the hashing function (see {@link Hash}).]]> + + + + + this hash function. A NOOP]]> + + + + + + + + + + + + + + + + + + + + + + + + + Builds a key with a default weight. + @param value The byte value of this key.]]> + + + + + + Builds a key with a specified weight. + @param value The value of this key. + @param weight The weight associated to this key.]]> + + + + + + + + + + + + this key.]]> + + + + + this key.]]> + + + + + + this key with a specified value. + @param weight The increment.]]> + + + + + this key by one.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The idea is to randomly select a bit to reset.]]> + + + + + + The idea is to select the bit to reset that will generate the minimum + number of false negative.]]> + + + + + + The idea is to select the bit to reset that will remove the maximum number + of false positive.]]> + + + + + + The idea is to select the bit to reset that will, at the same time, remove + the maximum number of false positve while minimizing the amount of false + negative generated.]]> + + + + + Originally created by + European Commission One-Lab Project 034819.]]> + + + + + + + + + + + + + + this filter. + @param nbHash The number of hash function to consider. + @param hashType type of the hashing function (see + {@link org.apache.hadoop.util.hash.Hash}).]]> + + + + + + + + + this retouched Bloom filter. +

+ Invariant: if the false positive is null, nothing happens. + @param key The false positive key to add.]]> + + + + + + this retouched Bloom filter. + @param coll The collection of false positive.]]> + + + + + + this retouched Bloom filter. + @param keys The list of false positive.]]> + + + + + + this retouched Bloom filter. + @param keys The array of false positive.]]> + + + + + + + this retouched Bloom filter. + @param scheme The selective clearing scheme to apply.]]> + + + + + + + + + + + + retouched Bloom filter, as defined in the CoNEXT 2006 paper. +

+ It allows the removal of selected false positives at the cost of introducing + random false negatives, and with the benefit of eliminating some random false + positives at the same time. + +

+ Originally created by + European Commission One-Lab Project 034819. + + @see Filter The general behavior of a filter + @see BloomFilter A Bloom filter + @see RemoveScheme The different selective clearing algorithms + + @see Retouched Bloom Filters: Allowing Networked Applications to Trade Off Selected False Positives Against False Negatives]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + length, and + the provided seed value + @param bytes input bytes + @param length length of the valid bytes to consider + @param initval seed value + @return hash value]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The best hash table sizes are powers of 2. There is no need to do mod + a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. + For example, if you need only 10 bits, do + h = (h & hashmask(10)); + In which case, the hash table should have hashsize(10) elements. + +

If you are hashing n strings byte[][] k, do it like this: + for (int i = 0, h = 0; i < n; ++i) h = hash( k[i], h); + +

By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this + code any way you wish, private, educational, or commercial. It's free. + +

Use for hash table lookup, or anything where one collision in 2^^32 is + acceptable. Do NOT use for cryptographic purposes.]]> + + + + + + + + + + + lookup3.c, by Bob Jenkins, May 2006, Public Domain. + + You can use this free for any purpose. It's in the public domain. + It has no warranty. + + + @see lookup3.c + @see Hash Functions (and how this + function compares to others such as CRC, MD?, etc + @see Has update on the + Dr. Dobbs Article]]> + + + + + + + + + + + + + + + + The C version of MurmurHash 2.0 found at that site was ported + to Java by Andrzej Bialecki (ab at getopt org).

]]> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker, + as {@link JobTracker.State} + + @return the current state of the JobTracker.]]> + + + + + JobTracker + + @return the size of heap memory used by the JobTracker]]> + + + + + JobTracker + + @return the configured size of max heap memory that can be used by the JobTracker]]> + + + + + + + + + + + + ClusterStatus provides clients with information such as: +
    +
  1. + Size of the cluster. +
  2. +
  3. + Name of the trackers. +
  4. +
  5. + Task capacity of the cluster. +
  6. +
  7. + The number of currently running map & reduce tasks. +
  8. +
  9. + State of the JobTracker. +
  10. +

+ +

Clients can query for the latest ClusterStatus, via + {@link JobClient#getClusterStatus()}.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter can be of + any {@link Enum} type.

+ +

Counters are bunched into {@link Group}s, each comprising of + counters from a particular Enum class. + @deprecated Use {@link org.apache.hadoop.mapreduce.Counters} instead.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Group of counters, comprising of counters from a particular + counter {@link Enum} class. + +

Grouphandles localization of the class name and the + counter names.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param fs the file system that the file is on + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobConf, int)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(FileSystem, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s. + @deprecated Use {@link org.apache.hadoop.mapreduce.lib.input.FileInputFormat} + instead.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Note: The following is valid only if the {@link OutputCommitter} + is {@link FileOutputCommitter}. If OutputCommitter is not + a FileOutputCommitter, the task's temporary output + directory is same as {@link #getOutputPath(JobConf)} i.e. + ${mapred.output.dir}$

+ +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in ${mapred.work.output.dir} during execution + of his reduce-task i.e. via {@link #getWorkOutputPath(JobConf)}, and the + framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

Note: the value of ${mapred.work.output.dir} during + execution of a particular task-attempt is actually + ${mapred.output.dir}/_temporary/_{$taskid}, and this value is + set by the map-reduce framework. So, just create any side-files in the + path returned by {@link #getWorkOutputPath(JobConf)} from map/reduce + task to take advantage of this feature.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+
+ + + + + + + + + + + + + The generated name can be used to create custom files from within the + different tasks for the job, the names for different tasks will not collide + with each other.

+ +

The given name is postfixed with the task type, 'm' for maps, 'r' for + reduces and the task partition number. For example, give a name 'test' + running on the first map o the job the generated name will be + 'test-m-00000'.

+ + @param conf the configuration for the job. + @param name the name to make unique. + @return a unique name accross all tasks of the job.]]> +
+
+ + + + + The path can be used to create custom files from within the map and + reduce tasks. The path name will be unique for each task. The path parent + will be the job output directory.

ls + +

This method uses the {@link #getUniqueName} method to make the file name + unique for the task.

+ + @param conf the configuration for the job. + @param name the name for the file. + @return a unique path accross all tasks of the job.]]> +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. + + @param job job configuration. + @param numSplits the desired number of splits, a hint. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + It is the responsibility of the RecordReader to respect + record boundaries while processing the logical split to present a + record-oriented view to the individual task.

+ + @param split the {@link InputSplit} + @param job the job that this split belongs to + @return a {@link RecordReader}]]> +
+
+ + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibilty to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see JobClient + @see FileInputFormat + @deprecated Use {@link org.apache.hadoop.mapreduce.InputFormat} instead.]]> + + + + + + + + + + InputSplit. + + @return the number of bytes in the input split. + @throws IOException]]> + + + + + + InputSplit is + located as an array of Strings. + @throws IOException]]> + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader + @deprecated Use {@link org.apache.hadoop.mapreduce.InputSplit} instead.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jobid doesn't correspond to any known job. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobClient is the primary interface for the user-job to interact + with the {@link JobTracker}. + + JobClient provides facilities to submit jobs, track their + progress, access component-tasks' reports/logs, get the Map-Reduce cluster + status information etc. + +

The job submission process involves: +

    +
  1. + Checking the input and output specifications of the job. +
  2. +
  3. + Computing the {@link InputSplit}s for the job. +
  4. +
  5. + Setup the requisite accounting information for the {@link DistributedCache} + of the job, if necessary. +
  6. +
  7. + Copying the job's jar and configuration to the map-reduce system directory + on the distributed file-system. +
  8. +
  9. + Submitting the job to the JobTracker and optionally monitoring + it's status. +
  10. +

+ + Normally the user creates the application, describes various facets of the + job via {@link JobConf} and then uses the JobClient to submit + the job and monitor its progress. + +

Here is an example on how to use JobClient:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     job.setInputPath(new Path("in"));
+     job.setOutputPath(new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+
+     // Submit the job, then poll for progress until the job is complete
+     JobClient.runJob(job);
+ 

+ +

Job Control

+ +

At times clients would chain map-reduce jobs to accomplish complex tasks + which cannot be done via a single map-reduce job. This is fairly easy since + the output of the job, typically, goes to distributed file-system and that + can be used as the input for the next job.

+ +

However, this also means that the onus on ensuring jobs are complete + (success/failure) lies squarely on the clients. In such situations the + various job-control options are: +

    +
  1. + {@link #runJob(JobConf)} : submits the job and returns only after + the job has completed. +
  2. +
  3. + {@link #submitJob(JobConf)} : only submits the job, then poll the + returned handle to the {@link RunningJob} to query status and make + scheduling decisions. +
  4. +
  5. + {@link JobConf#setJobEndNotificationURI(String)} : setup a notification + on job-completion, thus avoiding polling. +
  6. +

+ + @see JobConf + @see ClusterStatus + @see Tool + @see DistributedCache]]> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If the parameter {@code loadDefaults} is false, the new instance + will not load resources from the default files. + + @param loadDefaults specifies whether to load from the default files]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if framework should keep the intermediate files + for failed tasks, false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the outputs of the maps are to be compressed, + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This comparator should be provided if the equivalence rules for keys + for sorting the intermediates are different from those for grouping keys + before each call to + {@link Reducer#reduce(Object, java.util.Iterator, OutputCollector, Reporter)}.

+ +

For key-value pairs (K1,V1) and (K2,V2), the values (V1, V2) are passed + in a single call to the reduce function if K1 and K2 compare as equal.

+ +

Since {@link #setOutputKeyComparatorClass(Class)} can be used to control + how keys are sorted, this can be used in conjunction to simulate + secondary sort on values.

+ +

Note: This is not a guarantee of the reduce sort being + stable in any sense. (In any case, with the order of available + map-outputs to the reduce being non-deterministic, it wouldn't make + that much sense.)

+ + @param theClass the comparator class to be used for grouping keys. + It should implement RawComparator. + @see #setOutputKeyComparatorClass(Class)]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. Typically the combiner is same as the + the {@link Reducer} for the job i.e. {@link #getReducerClass()}. + + @return the user-defined combiner class used to combine map-outputs.]]> + + + + + + combiner class used to combine map-outputs + before being sent to the reducers. + +

The combiner is an application-specified aggregation operation, which + can help cut down the amount of data transferred between the + {@link Mapper} and the {@link Reducer}, leading to better performance.

+ +

The framework may invoke the combiner 0, 1, or multiple times, in both + the mapper and reducer tasks. In general, the combiner is called as the + sort/merge result is written to disk. The combiner must: +

    +
  • be side-effect free
  • +
  • have the same input and output key types and the same input and + output value types
  • +

+ +

Typically the combiner is same as the Reducer for the + job i.e. {@link #setReducerClass(Class)}.

+ + @param theClass the user-defined combiner class used to combine + map-outputs.]]> +
+
+ + + true. + + @return true if speculative execution be used for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on, else false.]]> + + + + + true. + + @return true if speculative execution be + used for this job for map tasks, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for map tasks, + else false.]]> + + + + + true. + + @return true if speculative execution be used + for reduce tasks for this job, + false otherwise.]]> + + + + + + true if speculative execution + should be turned on for reduce tasks, + else false.]]> + + + + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + Note: This is only a hint to the framework. The actual + number of spawned map tasks depends on the number of {@link InputSplit}s + generated by the job's {@link InputFormat#getSplits(JobConf, int)}. + + A custom {@link InputFormat} is typically used to accurately control + the number of map tasks for the job.

+ +

How many maps?

+ +

The number of maps is usually driven by the total size of the inputs + i.e. total number of blocks of the input files.

+ +

The right level of parallelism for maps seems to be around 10-100 maps + per-node, although it has been set up to 300 or so for very cpu-light map + tasks. Task setup takes awhile, so it is best if the maps take at least a + minute to execute.

+ +

The default behavior of file-based {@link InputFormat}s is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of input files. However, the {@link FileSystem} blocksize of the + input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Thus, if you expect 10TB of input data and have a blocksize of 128MB, + you'll end up with 82,000 maps, unless {@link #setNumMapTasks(int)} is + used to set it even higher.

+ + @param n the number of map tasks for this job. + @see InputFormat#getSplits(JobConf, int) + @see FileInputFormat + @see FileSystem#getDefaultBlockSize() + @see FileStatus#getBlockSize()]]> +
+
+ + + 1. + + @return the number of reduce tasks for this job.]]> + + + + + + How many reduces? + +

The right number of reduces seems to be 0.95 or + 1.75 multiplied by (<no. of nodes> * + + mapred.tasktracker.reduce.tasks.maximum). +

+ +

With 0.95 all of the reduces can launch immediately and + start transfering map outputs as the maps finish. With 1.75 + the faster nodes will finish their first round of reduces and launch a + second wave of reduces doing a much better job of load balancing.

+ +

Increasing the number of reduces increases the framework overhead, but + increases load balancing and lowers the cost of failures.

+ +

The scaling factors above are slightly less than whole numbers to + reserve a few reduce slots in the framework for speculative-tasks, failures + etc.

+ +

Reducer NONE

+ +

It is legal to set the number of reduce-tasks to zero.

+ +

In this case the output of the map-tasks directly go to distributed + file-system, to the path set by + {@link FileOutputFormat#setOutputPath(JobConf, Path)}. Also, the + framework doesn't sort the map-outputs before writing it out to HDFS.

+ + @param n the number of reduce tasks for this job.]]> +
+
+ + + mapred.map.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per map task.]]> + + + + + + + + + + + mapred.reduce.max.attempts + property. If this property is not already set, the default is 4 attempts. + + @return the max number of attempts per reduce task.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noFailures, the + tasktracker is blacklisted for this job. + + @param noFailures maximum no. of failures of a given job per tasktracker.]]> + + + + + blacklisted for this job. + + @return the maximum no. of failures of a given job per tasktracker.]]> + + + + + failed. + + Defaults to zero, i.e. any failed map-task results in + the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of map tasks that can fail without + the job being aborted.]]> + + + + + failed. + + Defaults to zero, i.e. any failed reduce-task results + in the job being declared as {@link JobStatus#FAILED}. + + @return the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + failed. + + @param percent the maximum percentage of reduce tasks that can fail without + the job being aborted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The debug script can aid debugging of failed map tasks. The script is + given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script needs to be symlinked.

+ +

Here is an example on how to submit a script +

+ job.setMapDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param mDbgScript the script name]]> +
+
+ + + + + + + + + The debug script can aid debugging of failed reduce tasks. The script + is given task's stdout, stderr, syslog, jobconf files as arguments.

+ +

The debug command, run on the node where the map failed, is:

+

+ $script $stdout $stderr $syslog $jobconf. +

+ +

The script file is distributed through {@link DistributedCache} + APIs. The script file needs to be symlinked

+ +

Here is an example on how to submit a script +

+ job.setReduceDebugScript("./myscript");
+ DistributedCache.createSymlink(job);
+ DistributedCache.addCacheFile("/debug/scripts/myscript#myscript");
+ 

+ + @param rDbgScript the script name]]> +
+
+ + + + + + + + null if it hasn't + been set. + @see #setJobEndNotificationURI(String)]]> + + + + + + The uri can contain 2 special parameters: $jobId and + $jobStatus. Those, if present, are replaced by the job's + identifier and completion-status respectively.

+ +

This is typically used by application-writers to implement chaining of + Map-Reduce jobs in an asynchronous manner.

+ + @param uri the job end notification uri + @see JobStatus + @see Job Completion and Chaining]]> +
+
+ + + + When a job starts, a shared directory is created at location + + ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/ . + This directory is exposed to the users through + job.local.dir . + So, the tasks can use this space + as scratch space and share files among them.

+ This value is available as System property also. + + @return The localized job specific shared directory]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If a job doesn't specify its virtual memory requirement by setting + {@link #MAPRED_TASK_MAXVMEM_PROPERTY} to {@link #DISABLED_MEMORY_LIMIT}, + tasks are assured a memory limit set to this property. This property is + disabled by default, and if not explicitly set to a valid value by the + administrators and if a job doesn't specify its virtual memory + requirements, the job's tasks will not be assured anything and may be + killed by a TT that intends to control the total memory usage of the tasks + via memory management functionality. + +

+ + This value should in general be less than the cluster-wide configuration + {@link #UPPER_LIMIT_ON_TASK_VMEM_PROPERTY} . If not or if it not set, + TaskTracker's memory management may be disabled and a scheduler's memory + based scheduling decisions will be affected. Please refer to the + documentation of the configured scheduler to see how this property is used.]]> + + + + + + + This value will be used by TaskTrackers for monitoring the memory usage of + tasks of this jobs. If a TaskTracker's memory management functionality is + enabled, each task of this job will be allowed to use a maximum virtual + memory specified by this property. If the task's memory usage goes over + this value, the task will be failed by the TT. If not set, the cluster-wide + configuration {@link #MAPRED_TASK_DEFAULT_MAXVMEM_PROPERTY} is used as the + default value for memory requirements. If this property cascaded with + {@link #MAPRED_TASK_DEFAULT_MAXVMEM_PROPERTY} becomes equal to -1, job's + tasks will not be assured anything and may be killed by a TT that intends + to control the total memory usage of the tasks via memory management + functionality. If the memory management functionality is disabled on a TT, + this value is ignored. + +

+ + This value should also be not more than the cluster-wide configuration + {@link #UPPER_LIMIT_ON_TASK_VMEM_PROPERTY} which has to be set by the site + administrators. + +

+ + This value may be used by schedulers that support scheduling based on job's + memory requirements. In general, a task of this job will be scheduled on a + TaskTracker only if the amount of virtual memory still unoccupied on the + TaskTracker is greater than or equal to this value. But different + schedulers can take different decisions. Please refer to the documentation + of the scheduler being configured to see if it does memory based scheduling + and if it does, how this property is used by that scheduler. + + @see #setMaxVirtualMemoryForTask(long) + @see #getMaxVirtualMemoryForTask()]]> + + + + + + + This value may be used by schedulers that support scheduling based on job's + memory requirements. In general, a task of this job will be scheduled on a + TaskTracker, only if the amount of physical memory still unoccupied on the + TaskTracker is greater than or equal to this value. But different + schedulers can take different decisions. Please refer to the documentation + of the scheduler being configured to see how it does memory based + scheduling and how this variable is used by that scheduler. + + @see #setMaxPhysicalMemoryForTask(long) + @see #getMaxPhysicalMemoryForTask()]]> + + + + + + + If it is not set on a TaskTracker, TaskTracker's memory management will be + disabled.]]> + + + + JobConf is the primary interface for a user to describe a + map-reduce job to the Hadoop framework for execution. The framework tries to + faithfully execute the job as-is described by JobConf, however: +

    +
  1. + Some configuration parameters might have been marked as + + final by administrators and hence cannot be altered. +
  2. +
  3. + While some job parameters are straight-forward to set + (e.g. {@link #setNumReduceTasks(int)}), some parameters interact subtly + rest of the framework and/or job-configuration and is relatively more + complex for the user to control finely (e.g. {@link #setNumMapTasks(int)}). +
  4. +

+ +

JobConf typically specifies the {@link Mapper}, combiner + (if any), {@link Partitioner}, {@link Reducer}, {@link InputFormat} and + {@link OutputFormat} implementations to be used etc. + +

Optionally JobConf is used to specify other advanced facets + of the job such as Comparators to be used, files to be put in + the {@link DistributedCache}, whether or not intermediate and/or job outputs + are to be compressed (and how), debugability via user-provided scripts + ( {@link #setMapDebugScript(String)}/{@link #setReduceDebugScript(String)}), + for doing post-processing on task logs, task's stdout, stderr, syslog. + and etc.

+ +

Here is an example on how to configure a job via JobConf:

+

+     // Create a new JobConf
+     JobConf job = new JobConf(new Configuration(), MyJob.class);
+     
+     // Specify various job-specific parameters     
+     job.setJobName("myjob");
+     
+     FileInputFormat.setInputPaths(job, new Path("in"));
+     FileOutputFormat.setOutputPath(job, new Path("out"));
+     
+     job.setMapperClass(MyJob.MyMapper.class);
+     job.setCombinerClass(MyJob.MyReducer.class);
+     job.setReducerClass(MyJob.MyReducer.class);
+     
+     job.setInputFormat(SequenceFileInputFormat.class);
+     job.setOutputFormat(SequenceFileOutputFormat.class);
+ 

+ + @see JobClient + @see ClusterStatus + @see Tool + @see DistributedCache + @deprecated Use {@link Configuration} instead]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + any job + run on the jobtracker started at 200707121733, we would use : +
 
+ JobID.getTaskIDsPattern("200707121733", null);
+ 
+ which will return : +
 "job_200707121733_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @return a regex pattern matching JobIDs]]> +
+
+ + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "N/A" + + @return Scheduling information associated to particular Job Queue]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zero. + + @param conf configuration for the JobTracker. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Output pairs need not be of the same types as input pairs. A given + input pair may map to zero or many output pairs. Output pairs are + collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the input key. + @param value the input value. + @param output collects mapped keys and values. + @param reporter facility to report progress.]]> +
+ + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link JobConf} for the + job via the {@link JobConfigurable#configure(JobConf)} and initialize + themselves. Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

The framework then calls + {@link #map(Object, Object, OutputCollector, Reporter)} + for each key/value pair in the InputSplit for that task.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the grouping by specifying + a Comparator via + {@link JobConf#setOutputKeyComparatorClass(Class)}.

+ +

The grouped Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link JobConf#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

The intermediate, grouped outputs are always stored in + {@link SequenceFile}s. Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the JobConf.

+ +

If the job has + zero + reduces then the output of the Mapper is directly written + to the {@link FileSystem} without grouping by keys.

+ +

Example:

+

+     public class MyMapper<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Mapper<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+       
+       private String mapTaskId;
+       private String inputFile;
+       private int noRecords = 0;
+       
+       public void configure(JobConf job) {
+         mapTaskId = job.get("mapred.task.id");
+         inputFile = job.get("map.input.file");
+       }
+       
+       public void map(K key, V val,
+                       OutputCollector<K, V> output, Reporter reporter)
+       throws IOException {
+         // Process the <key, value> pair (assume this takes a while)
+         // ...
+         // ...
+         
+         // Let the framework know that we are alive, and kicking!
+         // reporter.progress();
+         
+         // Process some more
+         // ...
+         // ...
+         
+         // Increment the no. of <key, value> pairs processed
+         ++noRecords;
+
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+        
+         // Every 100 records update application-level status
+         if ((noRecords%100) == 0) {
+           reporter.setStatus(mapTaskId + " processed " + noRecords + 
+                              " from input-file: " + inputFile); 
+         }
+         
+         // Output the result
+         output.collect(key, val);
+       }
+     }
+ 

+ +

Applications may write a custom {@link MapRunnable} to exert greater + control on map processing e.g. multi-threaded Mappers etc.

+ + @see JobConf + @see InputFormat + @see Partitioner + @see Reducer + @see MapReduceBase + @see MapRunnable + @see SequenceFile + @deprecated Use {@link org.apache.hadoop.mapreduce.Mapper} instead.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + Provides default no-op implementations for a few methods, most non-trivial + applications need to override some of them.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + <key, value> pairs. + +

Mapping of input records to output records is complete when this method + returns.

+ + @param input the {@link RecordReader} to read the input records. + @param output the {@link OutputCollector} to collect the outputrecords. + @param reporter {@link Reporter} to report progress, status-updates etc. + @throws IOException]]> +
+
+ + Custom implementations of MapRunnable can exert greater + control on map processing e.g. multi-threaded, asynchronous mappers etc.

+ + @see Mapper + @deprecated Use {@link org.apache.hadoop.mapreduce.Mapper} instead.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nearly + equal content length.
+ Subclasses implement {@link #getRecordReader(InputSplit, JobConf, Reporter)} + to construct RecordReader's for MultiFileSplit's. + @see MultiFileSplit + @deprecated Use {@link org.apache.hadoop.mapred.lib.CombineFileInputFormat} instead]]> +
+
+ + + + + + + + + + + + + MultiFileSplit can be used to implement {@link RecordReader}'s, with + reading one record per file. + @see FileSplit + @see MultiFileInputFormat + @deprecated Use {@link org.apache.hadoop.mapred.lib.CombineFileSplit} instead]]> + + + + + + + + + + + + + + + <key, value> pairs output by {@link Mapper}s + and {@link Reducer}s. + +

OutputCollector is the generalization of the facility + provided by the Map-Reduce framework to collect data output by either the + Mapper or the Reducer i.e. intermediate outputs + or the output of the job.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputCommitter describes the commit of task output for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputCommitter of + the job to:

+

    +
  1. + Setup the job during initialization. For example, create the temporary + output directory for the job during the initialization of the job. +
  2. +
  3. + Cleanup the job after the job completion. For example, remove the + temporary output directory after the job completion. +
  4. +
  5. + Setup the task temporary output. +
  6. +
  7. + Check whether a task needs a commit. This is to avoid the commit + procedure if a task does not need commit. +
  8. +
  9. + Commit of the task output. +
  10. +
  11. + Discard the task commit. +
  12. +
+ + @see FileOutputCommitter + @see JobContext + @see TaskAttemptContext + @deprecated Use {@link org.apache.hadoop.mapreduce.OutputCommitter} instead.]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param ignored + @param job job configuration. + @throws IOException when output should not be attempted]]> +
+
+ + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter + @see JobConf + @deprecated Use {@link org.apache.hadoop.mapreduce.OutputFormat} instead.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be paritioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer + @deprecated Use {@link org.apache.hadoop.mapreduce.Partitioner} instead.]]> +
+
+ + + + + + + + + + + + + + + + + + + true if there exists a key/value, + false otherwise. + @throws IOException]]> + + + + + + + + + + + + + + + RawKeyValueIterator is an iterator used to iterate over + the raw keys and values during sort/merge of intermediate data.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 to 1.0. + @throws IOException]]> + + + + RecordReader reads <key, value> pairs from an + {@link InputSplit}. + +

RecordReader, typically, converts the byte-oriented view of + the input, provided by the InputSplit, and presents a + record-oriented view for the {@link Mapper} & {@link Reducer} tasks for + processing. It thus assumes the responsibility of processing record + boundaries and presenting the tasks with keys and values.

+ + @see InputSplit + @see InputFormat]]> +
+
+ + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param reporter facility to report progress. + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + Reduces values for a given key. + +

The framework calls this method for each + <key, (list of values)> pair in the grouped inputs. + Output values must be of the same type as input values. Input keys must + not be altered. The framework will reuse the key and value objects + that are passed into the reduce, therefore the application should clone + the objects they want to keep a copy of. In many cases, all values are + combined into zero or one value. +

+ +

Output pairs are collected with calls to + {@link OutputCollector#collect(Object,Object)}.

+ +

Applications can use the {@link Reporter} provided to report progress + or just indicate that they are alive. In scenarios where the application + takes an insignificant amount of time to process individual key/value + pairs, this is crucial since the framework might assume that the task has + timed-out and kill that task. The other way of avoiding this is to set + + mapred.task.timeout to a high-enough value (or even zero for no + time-outs).

+ + @param key the key. + @param values the list of values to reduce. + @param output to collect keys and combined values. + @param reporter facility to report progress.]]> +
+ + + The number of Reducers for the job is set by the user via + {@link JobConf#setNumReduceTasks(int)}. Reducer implementations + can access the {@link JobConf} for the job via the + {@link JobConfigurable#configure(JobConf)} method and initialize themselves. + Similarly they can use the {@link Closeable#close()} method for + de-initialization.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    Reducer is input the grouped output of a {@link Mapper}. + In the phase the framework, for each Reducer, fetches the + relevant partition of the output of all the Mappers, via HTTP. +

    +
  2. + +
  3. +

    Sort

    + +

    The framework groups Reducer inputs by keys + (since different Mappers may have output the same key) in this + stage.

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    If equivalence rules for keys while grouping the intermediates are + different from those for grouping keys before reduction, then one may + specify a Comparator via + {@link JobConf#setOutputValueGroupingComparator(Class)}.Since + {@link JobConf#setOutputKeyComparatorClass(Class)} can be used to + control how intermediate keys are grouped, these can be used in conjunction + to simulate secondary sort on values.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterator, OutputCollector, Reporter)} + method is called for each <key, (list of values)> pair in + the grouped inputs.

    +

    The output of the reduce task is typically written to the + {@link FileSystem} via + {@link OutputCollector#collect(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+     public class MyReducer<K extends WritableComparable, V extends Writable> 
+     extends MapReduceBase implements Reducer<K, V, K, V> {
+     
+       static enum MyCounters { NUM_RECORDS }
+        
+       private String reduceTaskId;
+       private int noKeys = 0;
+       
+       public void configure(JobConf job) {
+         reduceTaskId = job.get("mapred.task.id");
+       }
+       
+       public void reduce(K key, Iterator<V> values,
+                          OutputCollector<K, V> output, 
+                          Reporter reporter)
+       throws IOException {
+       
+         // Process
+         int noValues = 0;
+         while (values.hasNext()) {
+           V value = values.next();
+           
+           // Increment the no. of values for this key
+           ++noValues;
+           
+           // Process the <key, value> pair (assume this takes a while)
+           // ...
+           // ...
+           
+           // Let the framework know that we are alive, and kicking!
+           if ((noValues%10) == 0) {
+             reporter.progress();
+           }
+         
+           // Process some more
+           // ...
+           // ...
+           
+           // Output the <key, value> 
+           output.collect(key, value);
+         }
+         
+         // Increment the no. of <key, list of values> pairs processed
+         ++noKeys;
+         
+         // Increment counters
+         reporter.incrCounter(NUM_RECORDS, 1);
+         
+         // Every 100 keys update application-level status
+         if ((noKeys%100) == 0) {
+           reporter.setStatus(reduceTaskId + " processed " + noKeys);
+         }
+       }
+     }
+ 

+ + @see Mapper + @see Partitioner + @see Reporter + @see MapReduceBase + @deprecated Use {@link org.apache.hadoop.mapreduce.Reducer} instead.]]> +
+
+ + + + + + + + + + + + + + Counter of the given group/name.]]> + + + + + + + Counter of the given group/name.]]> + + + + + + + Enum. + @param amount A non-negative amount by which the counter is to + be incremented.]]> + + + + + + + + + + + + + + InputSplit that the map is reading from. + @throws UnsupportedOperationException if called outside a mapper]]> + + + + + + + + + {@link Mapper} and {@link Reducer} can use the Reporter + provided to report progress or just indicate that they are alive. In + scenarios where the application takes an insignificant amount of time to + process individual key/value pairs, this is crucial since the framework + might assume that the task has timed-out and kill that task. + +

Applications can also update {@link Counters} via the provided + Reporter .

+ + @see Progressable + @see Counters]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + progress of the job's cleanup-tasks, as a float between 0.0 + and 1.0. When all cleanup tasks have completed, the function returns 1.0. + + @return the progress of the job's cleanup-tasks. + @throws IOException]]> + + + + + + progress of the job's setup-tasks, as a float between 0.0 + and 1.0. When all setup tasks have completed, the function returns 1.0. + + @return the progress of the job's setup-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RunningJob is the user-interface to query for details on a + running Map-Reduce job. + +

Clients can get hold of RunningJob via the {@link JobClient} + and then query the running-job for details such as name, configuration, + progress etc.

+ + @see JobClient]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This allows the user to specify the key class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + This allows the user to specify the value class to be different + from the actual class ({@link BytesWritable}) used for writing

+ + @param conf the {@link JobConf} to modify + @param theClass the SequenceFile output key class.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f. The filtering criteria is + MD5(key) % f == 0.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + f using + the criteria record# % f == 0. + For example, if the frequency is 10, one out of 10 records is returned.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_MAP_PROCESSED_RECORDS}. + false otherwise.]]> + + + + + + + + + + + + + true if auto increment + {@link SkipBadRecords#COUNTER_REDUCE_PROCESSED_GROUPS}. + false otherwise.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadoop provides an optional mode of execution in which the bad records + are detected and skipped in further attempts. + +

This feature can be used when map/reduce tasks crashes deterministically on + certain input. This happens due to bugs in the map/reduce function. The usual + course would be to fix these bugs. But sometimes this is not possible; + perhaps the bug is in third party libraries for which the source code is + not available. Due to this, the task never reaches to completion even with + multiple attempts and complete data for that task is lost.

+ +

With this feature, only a small portion of data is lost surrounding + the bad record, which may be acceptable for some user applications. + see {@link SkipBadRecords#setMapperMaxSkipRecords(Configuration, long)}

+ +

The skipping mode gets kicked off after certain no of failures + see {@link SkipBadRecords#setAttemptsToStartSkipping(Configuration, int)}

+ +

In the skipping mode, the map/reduce task maintains the record range which + is getting processed at all times. Before giving the input to the + map/reduce function, it sends this record range to the Task tracker. + If task crashes, the Task tracker knows which one was the last reported + range. On further attempts that range get skipped.

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all task attempt IDs + of any jobtracker, in any job, of the first + map task, we would use : +
 
+ TaskAttemptID.getTaskAttemptIDsPattern(null, null, true, 1, null);
+ 
+ which will return : +
 "attempt_[^_]*_[0-9]*_m_000001_[0-9]*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @param attemptId the task attempt number, or null + @return a regex pattern matching TaskAttemptIDs]]> +
+
+ + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the first map task + of any jobtracker, of any job, we would use : +

 
+ TaskID.getTaskIDsPattern(null, null, true, 1);
+ 
+ which will return : +
 "task_[^_]*_[0-9]*_m_000001*" 
+ @param jtIdentifier jobTracker identifier, or null + @param jobId job number, or null + @param isMap whether the tip is a map, or null + @param taskId taskId number, or null + @return a regex pattern matching TaskIDs]]> +
+ + + + + + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hadoop.log.dir.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the Job was added.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([,]*) + func ::= tbl(,"") + class ::= @see java.lang.Class#forName(java.lang.String) + path ::= @see org.apache.hadoop.fs.Path#Path(java.lang.String) + } + Reads expression from the mapred.join.expr property and + user-supplied join types from mapred.join.define.<ident> + types. Paths supplied to tbl are given as input paths to the + InputFormat class listed. + @see #compose(java.lang.String, java.lang.Class, java.lang.String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ,

) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + + + + + (tbl(,),tbl(,),...,tbl(,)) }]]> + + + + mapred.join.define.<ident> to a classname. In the expression + mapred.join.expr, the identifier will be assumed to be a + ComposableRecordReader. + mapred.join.keycomparator can be a classname used to compare keys + in the join. + @see JoinRecordReader + @see MultiFilterRecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + capacity children to position + id in the parent reader. + The id of a root CompositeRecordReader is -1 by convention, but relying + on this is not recommended.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + override(S1,S2,S3) will prefer values + from S3 over S2, and values from S2 over S1 for all keys + emitted from all sources.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [,,...,]]]> + + + + + + + out. + TupleWritable format: + {@code + ...... + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain +

+ + @param job job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + The Mapper classes are invoked in a chained (or piped) fashion, the output of + the first becomes the input of the second, and so on until the last Mapper, + the output of the last Mapper will be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed in a chain. This enables having + reusable specialized Mappers that can be combined to perform composite + operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain. +

+ ChainMapper usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Reducer leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Reducer does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Reducer the configuration given for it, + reducerConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. + + @param job job's JobConf to add the Reducer class. + @param klass the Reducer class to add. + @param inputKeyClass reducer input key class. + @param inputValueClass reducer input value class. + @param outputKeyClass reducer output key class. + @param outputValueClass reducer output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param reducerConf a JobConf with the configuration for the Reducer + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + + + + + + + + It has to be specified how key and values are passed from one element of + the chain to the next, by value or by reference. If a Mapper leverages the + assumed semantics that the key and values are not modified by the collector + 'by value' must be used. If the Mapper does not expect this semantics, as + an optimization to avoid serialization and deserialization 'by reference' + can be used. +

+ For the added Mapper the configuration given for it, + mapperConf, have precedence over the job's JobConf. This + precedence is in effect when the task is running. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainMapper, this is done by the addMapper for the last mapper in the chain + . + + @param job chain job's JobConf to add the Mapper class. + @param klass the Mapper class to add. + @param inputKeyClass mapper input key class. + @param inputValueClass mapper input value class. + @param outputKeyClass mapper output key class. + @param outputValueClass mapper output value class. + @param byValue indicates if key/values should be passed by value + to the next Mapper in the chain, if any. + @param mapperConf a JobConf with the configuration for the Mapper + class. It is recommended to use a JobConf without default values using the + JobConf(boolean loadDefaults) constructor with FALSE.]]> + + + + + + + If this method is overriden super.configure(...) should be + invoked at the beginning of the overwriter method.]]> + + + + + + + + + + reduce(...) method of the Reducer with the + map(...) methods of the Mappers in the chain.]]> + + + + + + + If this method is overriden super.close() should be + invoked at the end of the overwriter method.]]> + + + + + For each record output by the Reducer, the Mapper classes are invoked in a + chained (or piped) fashion, the output of the first becomes the input of the + second, and so on until the last Mapper, the output of the last Mapper will + be written to the task's output. +

+ The key functionality of this feature is that the Mappers in the chain do not + need to be aware that they are executed after the Reducer or in a chain. + This enables having reusable specialized Mappers that can be combined to + perform composite operations within a single task. +

+ Special care has to be taken when creating chains that the key/values output + by a Mapper are valid for the following Mapper in the chain. It is assumed + all Mappers and the Reduce in the chain use maching output and input key and + value classes as no conversion is done by the chaining code. +

+ Using the ChainMapper and the ChainReducer classes is possible to compose + Map/Reduce jobs that look like [MAP+ / REDUCE MAP*]. And + immediate benefit of this pattern is a dramatic reduction in disk IO. +

+ IMPORTANT: There is no need to specify the output key/value classes for the + ChainReducer, this is done by the setReducer or the addMapper for the last + element in the chain. +

+ ChainReducer usage pattern: +

+

+ ...
+ conf.setJobName("chain");
+ conf.setInputFormat(TextInputFormat.class);
+ conf.setOutputFormat(TextOutputFormat.class);
+ 

+ JobConf mapAConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, AMap.class, LongWritable.class, Text.class, + Text.class, Text.class, true, mapAConf); +

+ JobConf mapBConf = new JobConf(false); + ... + ChainMapper.addMapper(conf, BMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, mapBConf); +

+ JobConf reduceConf = new JobConf(false); + ... + ChainReducer.setReducer(conf, XReduce.class, LongWritable.class, Text.class, + Text.class, Text.class, true, reduceConf); +

+ ChainReducer.addMapper(conf, CMap.class, Text.class, Text.class, + LongWritable.class, Text.class, false, null); +

+ ChainReducer.addMapper(conf, DMap.class, LongWritable.class, Text.class, + LongWritable.class, LongWritable.class, true, null); +

+ FileInputFormat.setInputPaths(conf, inDir); + FileOutputFormat.setOutputPath(conf, outDir); + ... +

+ JobClient jc = new JobClient(conf); + RunningJob job = jc.submitJob(conf); + ... +

]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RecordReader's for CombineFileSplit's. + @see CombineFileSplit]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + th Path]]> + + + + + + th Path]]> + + + + + + + + + + + th Path]]> + + + + + + + + + + + + + + + + + + + + + + + + + + CombineFileSplit can be used to implement {@link org.apache.hadoop.mapred.RecordReader}'s, + with reading one record per file. + @see org.apache.hadoop.mapred.FileSplit + @see CombineFileInputFormat]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + @param freq The frequency with which records will be emitted.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + This will read every split at the client, which is very expensive. + @param freq Probability with which a key will be chosen. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all splits. + Takes the first numSamples / numSplits records from each split. + @param numSamples Total number of samples to obtain from all selected + splits.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the name output is multi, false + if it is single. If the name output is not defined it returns + false]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + + + + + @param conf job conf to add the named output + @param namedOutput named output name, it has to be a word, letters + and numbers only, cannot be the word 'part' as + that is reserved for the + default output. + @param outputFormatClass OutputFormat class. + @param keyClass key class + @param valueClass value class]]> + + + + + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + @param conf job conf to enableadd the named output. + @param enabled indicates if the counters will be enabled or not.]]> +
+
+ + + + + By default these counters are disabled. +

+ MultipleOutputs supports counters, by default the are disabled. + The counters group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. + + + @param conf job conf to enableadd the named output. + @return TRUE if the counters are enabled, FALSE if they are disabled.]]> +
+
+ + + + + + + + + + + + + @param namedOutput the named output name + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + + + + + @param namedOutput the named output name + @param multiName the multi name part + @param reporter the reporter + @return the output collector for the given named output + @throws IOException thrown if output collector could not be created]]> + + + + + + + If overriden subclasses must invoke super.close() at the + end of their close() + + @throws java.io.IOException thrown if any of the MultipleOutput files + could not be closed properly.]]> + + + + OutputCollector passed to + the map() and reduce() methods of the + Mapper and Reducer implementations. +

+ Each additional output, or named output, may be configured with its own + OutputFormat, with its own key class and with its own value + class. +

+ A named output can be a single file or a multi file. The later is refered as + a multi named output. +

+ A multi named output is an unbound set of files all sharing the same + OutputFormat, key class and value class configuration. +

+ When named outputs are used within a Mapper implementation, + key/values written to a name output are not part of the reduce phase, only + key/values written to the job OutputCollector are part of the + reduce phase. +

+ MultipleOutputs supports counters, by default the are disabled. The counters + group is the {@link MultipleOutputs} class name. +

+ The names of the counters are the same as the named outputs. For multi + named outputs the name of the counter is the concatenation of the named + output, and underscore '_' and the multiname. +

+ Job configuration usage pattern is: +

+
+ JobConf conf = new JobConf();
+
+ conf.setInputPath(inDir);
+ FileOutputFormat.setOutputPath(conf, outDir);
+
+ conf.setMapperClass(MOMap.class);
+ conf.setReducerClass(MOReduce.class);
+ ...
+
+ // Defines additional single text based output 'text' for the job
+ MultipleOutputs.addNamedOutput(conf, "text", TextOutputFormat.class,
+ LongWritable.class, Text.class);
+
+ // Defines additional multi sequencefile based output 'sequence' for the
+ // job
+ MultipleOutputs.addMultiNamedOutput(conf, "seq",
+   SequenceFileOutputFormat.class,
+   LongWritable.class, Text.class);
+ ...
+
+ JobClient jc = new JobClient();
+ RunningJob job = jc.submitJob(conf);
+
+ ...
+ 
+

+ Job configuration usage pattern is: +

+
+ public class MOReduce implements
+   Reducer<WritableComparable, Writable> {
+ private MultipleOutputs mos;
+
+ public void configure(JobConf conf) {
+ ...
+ mos = new MultipleOutputs(conf);
+ }
+
+ public void reduce(WritableComparable key, Iterator<Writable> values,
+ OutputCollector output, Reporter reporter)
+ throws IOException {
+ ...
+ mos.getCollector("text", reporter).collect(key, new Text("Hello"));
+ mos.getCollector("seq", "A", reporter).collect(key, new Text("Bye"));
+ mos.getCollector("seq", "B", reporter).collect(key, new Text("Chau"));
+ ...
+ }
+
+ public void close() throws IOException {
+ mos.close();
+ ...
+ }
+
+ }
+ 
]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Map implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured to use this MapRunnable class (using + the JobConf.setMapRunnerClass method) and + the number of thread the thread-pool can use with the + mapred.map.multithreadedrunner.threads property, its default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pairs. Uses + {@link StringTokenizer} to break text into tokens. + @deprecated Use + {@link org.apache.hadoop.mapreduce.lib.map.TokenCounterMapper} instead.]]> + + + + + + + + + + + + total.order.partitioner.natural.order is not false, a trie + of the first total.order.partitioner.max.trie.depth(2) + 1 bytes + will be built. Otherwise, keys will be located using a binary search of + the partition keyset using the {@link org.apache.hadoop.io.RawComparator} + defined for this job. The input file must be sorted with the same + comparator and contain {@link + org.apache.hadoop.mapred.JobConf#getNumReduceTasks} - 1 keys.]]> + + + + + + + + + + + + R reduces, there are R-1 + keys in the SequenceFile.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generateKeyValPairs(Object key, Object value); public void + configure(JobConfjob); } + + The package also provides a base class, ValueAggregatorBaseDescriptor, + implementing the above interface. The user can extend the base class and + implement generateKeyValPairs accordingly. + + The primary work of generateKeyValPairs is to emit one or more key/value + pairs based on the input key/value pair. The key in an output key/value pair + encode two pieces of information: aggregation type and aggregation id. The + value will be aggregated onto the aggregation id according the aggregation + type. + + This class offers a function to generate a map/reduce job using Aggregate + framework. The function takes the following parameters: input directory spec + input format (text or sequence file) output directory a file specifying the + user plugin class]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The job can be configured using the static methods in this class, + {@link DBInputFormat}, and {@link DBOutputFormat}. +

+ Alternatively, the properties can be set in the configuration with proper + values. + + @see DBConfiguration#configureDB(JobConf, String, String, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String) + @see DBInputFormat#setInput(JobConf, Class, String, String, String, String...) + @see DBOutputFormat#setOutput(JobConf, String, String...)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20070101 AND length > 0)' + @param orderBy the fieldNames in the orderBy clause. + @param fieldNames The field names in the table + @see #setInput(JobConf, Class, String, String)]]> + + + + + + + + + + + + + + DBInputFormat emits LongWritables containing the record number as + key and DBWritables as value. + + The SQL query, and input class can be using one of the two + setInput methods.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {@link DBOutputFormat} accepts <key,value> pairs, where + key has a type extending DBWritable. Returned {@link RecordWriter} + writes only the key to the database with a batch SQL query.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DBWritable. DBWritable, is similar to {@link Writable} + except that the {@link #write(PreparedStatement)} method takes a + {@link PreparedStatement}, and {@link #readFields(ResultSet)} + takes a {@link ResultSet}. +

+ Implementations are responsible for writing the fields of the object + to PreparedStatement, and reading the fields of the object from the + ResultSet. + +

Example:

+ If we have the following table in the database : +
+ CREATE TABLE MyTable (
+   counter        INTEGER NOT NULL,
+   timestamp      BIGINT  NOT NULL,
+ );
+ 
+ then we can read/write the tuples from/to the table with : +

+ public class MyWritable implements Writable, DBWritable {
+   // Some data     
+   private int counter;
+   private long timestamp;
+       
+   //Writable#write() implementation
+   public void write(DataOutput out) throws IOException {
+     out.writeInt(counter);
+     out.writeLong(timestamp);
+   }
+       
+   //Writable#readFields() implementation
+   public void readFields(DataInput in) throws IOException {
+     counter = in.readInt();
+     timestamp = in.readLong();
+   }
+       
+   public void write(PreparedStatement statement) throws SQLException {
+     statement.setInt(1, counter);
+     statement.setLong(2, timestamp);
+   }
+       
+   public void readFields(ResultSet resultSet) throws SQLException {
+     counter = resultSet.getInt(1);
+     timestamp = resultSet.getLong(2);
+   } 
+ }
+ 

]]> +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Counters represent global counters, defined either by the + Map-Reduce framework or applications. Each Counter is named by + an {@link Enum} and has a long for the value.

+ +

Counters are bunched into Groups, each comprising of + counters from a particular Enum class.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each {@link InputSplit} is then assigned to an individual {@link Mapper} + for processing.

+ +

Note: The split is a logical split of the inputs and the + input files are not physically split into chunks. For e.g. a split could + be <input-file-path, start, offset> tuple. The InputFormat + also creates the {@link RecordReader} to read the {@link InputSplit}. + + @param context job configuration. + @return an array of {@link InputSplit}s for the job.]]> + + + + + + + + + + + + + InputFormat describes the input-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the InputFormat of the + job to:

+

    +
  1. + Validate the input-specification of the job. +
  2. + Split-up the input file(s) into logical {@link InputSplit}s, each of + which is then assigned to an individual {@link Mapper}. +
  3. +
  4. + Provide the {@link RecordReader} implementation to be used to glean + input records from the logical InputSplit for processing by + the {@link Mapper}. +
  5. +
+ +

The default behavior of file-based {@link InputFormat}s, typically + sub-classes of {@link FileInputFormat}, is to split the + input into logical {@link InputSplit}s based on the total size, in + bytes, of the input files. However, the {@link FileSystem} blocksize of + the input files is treated as an upper bound for input splits. A lower bound + on the split size can be set via + + mapred.min.split.size.

+ +

Clearly, logical splits based on input-size is insufficient for many + applications since record boundaries are to respected. In such cases, the + application has to also implement a {@link RecordReader} on whom lies the + responsibility to respect record-boundaries and present a record-oriented + view of the logical InputSplit to the individual task. + + @see InputSplit + @see RecordReader + @see FileInputFormat]]> + + + + + + + + + + + + + + + + + + + + + + + InputSplit represents the data to be processed by an + individual {@link Mapper}. + +

Typically, it presents a byte-oriented view on the input and is the + responsibility of {@link RecordReader} of the job to process this and present + a record-oriented view. + + @see InputFormat + @see RecordReader]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + InputFormat to use + @throws IllegalStateException if the job is submitted]]> + + + + + + + OutputFormat to use + @throws IllegalStateException if the job is submitted]]> + + + + + + + Mapper to use + @throws IllegalStateException if the job is submitted]]> + + + + + + + + + + + + + + + + + + + + + + + + + Reducer to use + @throws IllegalStateException if the job is submitted]]> + + + + + + + Partitioner to use + @throws IllegalStateException if the job is submitted]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + progress of the job's map-tasks, as a float between 0.0 + and 1.0. When all map tasks have completed, the function returns 1.0. + + @return the progress of the job's map-tasks. + @throws IOException]]> + + + + + + progress of the job's reduce-tasks, as a float between 0.0 + and 1.0. When all reduce tasks have completed, the function returns 1.0. + + @return the progress of the job's reduce-tasks. + @throws IOException]]> + + + + + + true if the job is complete, else false. + @throws IOException]]> + + + + + + true if the job succeeded, else false. + @throws IOException]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JobTracker is lost]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1. + @return the number of reduce tasks for this job.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An example JobID is : + job_200707121733_0003 , which represents the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse JobID strings, but rather + use appropriate constructors or {@link #forName(String)} method. + + @see TaskID + @see TaskAttemptID + @see org.apache.hadoop.mapred.JobTracker#getNewJobId() + @see org.apache.hadoop.mapred.JobTracker#getStartTime()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + the key input type to the Mapper + @param the value input type to the Mapper + @param the key output type from the Mapper + @param the value output type from the Mapper]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Maps are the individual tasks which transform input records into a + intermediate records. The transformed intermediate records need not be of + the same type as the input records. A given input pair may map to zero or + many output pairs.

+ +

The Hadoop Map-Reduce framework spawns one map task for each + {@link InputSplit} generated by the {@link InputFormat} for the job. + Mapper implementations can access the {@link Configuration} for + the job via the {@link JobContext#getConfiguration()}. + +

The framework first calls + {@link #setup(org.apache.hadoop.mapreduce.Mapper.Context)}, followed by + {@link #map(Object, Object, Context)} + for each key/value pair in the InputSplit. Finally + {@link #cleanup(Context)} is called.

+ +

All intermediate values associated with a given output key are + subsequently grouped by the framework, and passed to a {@link Reducer} to + determine the final output. Users can control the sorting and grouping by + specifying two key {@link RawComparator} classes.

+ +

The Mapper outputs are partitioned per + Reducer. Users can control which keys (and hence records) go to + which Reducer by implementing a custom {@link Partitioner}. + +

Users can optionally specify a combiner, via + {@link Job#setCombinerClass(Class)}, to perform local aggregation of the + intermediate outputs, which helps to cut down the amount of data transferred + from the Mapper to the Reducer. + +

Applications can specify if and how the intermediate + outputs are to be compressed and which {@link CompressionCodec}s are to be + used via the Configuration.

+ +

If the job has zero + reduces then the output of the Mapper is directly written + to the {@link OutputFormat} without sorting by keys.

+ +

Example:

+

+ public class TokenCounterMapper 
+     extends Mapper{
+    
+   private final static IntWritable one = new IntWritable(1);
+   private Text word = new Text();
+   
+   public void map(Object key, Text value, Context context) throws IOException {
+     StringTokenizer itr = new StringTokenizer(value.toString());
+     while (itr.hasMoreTokens()) {
+       word.set(itr.nextToken());
+       context.collect(word, one);
+     }
+   }
+ }
+ 

+ +

Applications may override the {@link #run(Context)} method to exert + greater control on map processing e.g. multi-threaded Mappers + etc.

+ + @see InputFormat + @see JobContext + @see Partitioner + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputCommitter describes the commit of task output for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputCommitter of + the job to:

+

    +
  1. + Setup the job during initialization. For example, create the temporary + output directory for the job during the initialization of the job. +
  2. +
  3. + Cleanup the job after the job completion. For example, remove the + temporary output directory after the job completion. +
  4. +
  5. + Setup the task temporary output. +
  6. +
  7. + Check whether a task needs a commit. This is to avoid the commit + procedure if a task does not need commit. +
  8. +
  9. + Commit of the task output. +
  10. +
  11. + Discard the task commit. +
  12. +
+ + @see org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter + @see JobContext + @see TaskAttemptContext]]> +
+
+ + + + + + + + + + + + + + + + + + + This is to validate the output specification for the job when it is + a job is submitted. Typically checks that it does not already exist, + throwing an exception when it already exists, so that output is not + overwritten.

+ + @param context information about the job + @throws IOException when output should not be attempted]]> +
+
+ + + + + + + + + + OutputFormat describes the output-specification for a + Map-Reduce job. + +

The Map-Reduce framework relies on the OutputFormat of the + job to:

+

    +
  1. + Validate the output-specification of the job. For e.g. check that the + output directory doesn't already exist. +
  2. + Provide the {@link RecordWriter} implementation to be used to write out + the output files of the job. Output files are stored in a + {@link FileSystem}. +
  3. +
+ + @see RecordWriter]]> +
+
+ + + + + + + + + + + Typically a hash function on a all or a subset of the key.

+ + @param key the key to be partioned. + @param value the entry value. + @param numPartitions the total number of partitions. + @return the partition number for the key.]]> +
+
+ + Partitioner controls the partitioning of the keys of the + intermediate map-outputs. The key (or a subset of the key) is used to derive + the partition, typically by a hash function. The total number of partitions + is the same as the number of reduce tasks for the job. Hence this controls + which of the m reduce tasks the intermediate key (and hence the + record) is sent for reduction.

+ + @see Reducer]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @param ]]> + + + + + + + + + + + + + + + + + + + + + + RecordWriter to future operations. + + @param context the context of the task + @throws IOException]]> + + + + RecordWriter writes the output <key, value> pairs + to an output file. + +

RecordWriter implementations write the job outputs to the + {@link FileSystem}. + + @see OutputFormat]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the class of the input keys + @param the class of the input values + @param the class of the output keys + @param the class of the output values]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reducer implementations + can access the {@link Configuration} for the job via the + {@link JobContext#getConfiguration()} method.

+ +

Reducer has 3 primary phases:

+
    +
  1. + +

    Shuffle

    + +

    The Reducer copies the sorted output from each + {@link Mapper} using HTTP across the network.

    +
  2. + +
  3. +

    Sort

    + +

    The framework merge sorts Reducer inputs by + keys + (since different Mappers may have output the same key).

    + +

    The shuffle and sort phases occur simultaneously i.e. while outputs are + being fetched they are merged.

    + +
    SecondarySort
    + +

    To achieve a secondary sort on the values returned by the value + iterator, the application should extend the key with the secondary + key and define a grouping comparator. The keys will be sorted using the + entire key, but will be grouped using the grouping comparator to decide + which keys and values are sent in the same call to reduce.The grouping + comparator is specified via + {@link Job#setGroupingComparatorClass(Class)}. The sort order is + controlled by + {@link Job#setSortComparatorClass(Class)}.

    + + + For example, say that you want to find duplicate web pages and tag them + all with the url of the "best" known example. You would set up the job + like: +
      +
    • Map Input Key: url
    • +
    • Map Input Value: document
    • +
    • Map Output Key: document checksum, url pagerank
    • +
    • Map Output Value: url
    • +
    • Partitioner: by checksum
    • +
    • OutputKeyComparator: by checksum and then decreasing pagerank
    • +
    • OutputValueGroupingComparator: by checksum
    • +
    +
  4. + +
  5. +

    Reduce

    + +

    In this phase the + {@link #reduce(Object, Iterable, Context)} + method is called for each <key, (collection of values)> in + the sorted inputs.

    +

    The output of the reduce task is typically written to a + {@link RecordWriter} via + {@link Context#write(Object, Object)}.

    +
  6. +
+ +

The output of the Reducer is not re-sorted.

+ +

Example:

+

+ public class IntSumReducer extends Reducer {
+   private IntWritable result = new IntWritable();
+ 
+   public void reduce(Key key, Iterable values, 
+                      Context context) throws IOException {
+     int sum = 0;
+     for (IntWritable val : values) {
+       sum += val.get();
+     }
+     result.set(sum);
+     context.collect(key, result);
+   }
+ }
+ 

+ + @see Mapper + @see Partitioner]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An example TaskAttemptID is : + attempt_200707121733_0003_m_000005_0 , which represents the + zeroth task attempt for the fifth map task in the third job + running at the jobtracker started at 200707121733. +

+ Applications should never construct or parse TaskAttemptID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An example TaskID is : + task_200707121733_0003_m_000005 , which represents the + fifth map task in the third job running at the jobtracker + started at 200707121733. +

+ Applications should never construct or parse TaskID strings + , but rather use appropriate constructors or {@link #forName(String)} + method. + + @see JobID + @see TaskAttemptID]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the input key type for the task + @param the input value type for the task + @param the output key type for the task + @param the output value type for the task]]> + + + + + + + + + + + + + + + + + + + FileInputFormat implementations can override this and return + false to ensure that individual input files are never split-up + so that {@link Mapper}s process entire files. + + @param context the job context + @param filename the file name to check + @return is this file splitable?]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FileInputFormat is the base class for all file-based + InputFormats. This provides a generic implementation of + {@link #getSplits(JobContext)}. + Subclasses of FileInputFormat can also override the + {@link #isSplitable(JobContext, Path)} method to ensure input-files are + not split-up and are processed as a whole by {@link Mapper}s.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + the map's input key type + @param the map's input value type + @param the map's output key type + @param the map's output value type + @param job the job + @return the mapper class to run]]> + + + + + + + the map input key type + @param the map input value type + @param the map output key type + @param the map output value type + @param job the job to modify + @param cls the class to use as the mapper]]> + + + + + + + + + + + + + It can be used instead of the default implementation, + @link org.apache.hadoop.mapred.MapRunner, when the Map operation is not CPU + bound in order to improve throughput. +

+ Mapper implementations using this MapRunnable must be thread-safe. +

+ The Map-Reduce job has to be configured with the mapper to use via + {@link #setMapperClass(Configuration, Class)} and + the number of thread the thread-pool can use with the + {@link #getNumberOfThreads(Configuration) method. The default + value is 10 threads. +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if the job output should be compressed, + false otherwise]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasks' Side-Effect Files + +

Some applications need to create/write-to side-files, which differ from + the actual job-outputs. + +

In such cases there could be issues with 2 instances of the same TIP + (running simultaneously e.g. speculative tasks) trying to open/write-to the + same file (path) on HDFS. Hence the application-writer will have to pick + unique names per task-attempt (e.g. using the attemptid, say + attempt_200709221812_0001_m_000000_0), not just per TIP.

+ +

To get around this the Map-Reduce framework helps the application-writer + out by maintaining a special + ${mapred.output.dir}/_temporary/_${taskid} + sub-directory for each task-attempt on HDFS where the output of the + task-attempt goes. On successful completion of the task-attempt the files + in the ${mapred.output.dir}/_temporary/_${taskid} (only) + are promoted to ${mapred.output.dir}. Of course, the + framework discards the sub-directory of unsuccessful task-attempts. This + is completely transparent to the application.

+ +

The application-writer can take advantage of this by creating any + side-files required in a work directory during execution + of his task i.e. via + {@link #getWorkOutputPath(TaskInputOutputContext)}, and + the framework will move them out similarly - thus she doesn't have to pick + unique paths per task-attempt.

+ +

The entire discussion holds true for maps of jobs with + reducer=NONE (i.e. 0 reduces) since output of the map, in that case, + goes directly to HDFS.

+ + @return the {@link Path} to the task's temporary output directory + for the map-reduce job.]]> +
+ + + + + + + + + The path can be used to create custom files from within the map and + reduce tasks. The path name will be unique for each task. The path parent + will be the job output directory.

ls + +

This method uses the {@link #getUniqueFile} method to make the file name + unique for the task.

+ + @param context the context for the task. + @param name the name for the file. + @param extension the extension for the file + @return a unique path accross all tasks of the job.]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool supports archiving and anaylzing (sort/grep) of log-files. + It takes as input + a) Input uri which will serve uris of the logs to be archived. + b) Output directory (not mandatory). + b) Directory on dfs to archive the logs. + c) The sort/grep patterns for analyzing the files and separator for boundaries. + Usage: + Logalyzer -archive -archiveDir -analysis -logs -grep -sort -separator +

]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/kfs-0.2.2.jar b/lib/kfs-0.2.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..aa32e74baf26d902fc7a195eaa7c9719212b8182 GIT binary patch literal 11428 zcmai4WmsKFwj}|AyL)hVcM0z9!7uLa1cGaDcXxMpcjsakch^A3OV9L7_j|9Wr|wsE z&X2v;-c{%O>Q?Q&6lK66V8MQCJFQgQ|9JU*fB}O5lM_=Frk9czXM7t215^Cn6dDZl z%alItNbTD%(}`al{a63DshqI9l(?9RDubN3O;kJVduDXe7oj)eLd>wy@KB(bu%;;9 zpnBhh+Sha_DcB!gcDN`FnNFsrb{(A}46pFiz-^tPzyi^iYEg;#Eq}vcd#!5K z7UMUn-wl6z*g2Ux{RvUweEYwg3|Nj0qrVgM9`h#X-$1FMixIKI>g zcsmNAips{dCGB?LVBwM_ebXYWx(FMj&SRmrPO|#B(fQ8YU}41Pitm{(o3g>9c&Oiv zr#^3XZgD@-ZFSyW0o@3}m}Cg>dU0Af1K3*_WgWWxp=9~6g`tr~_xec)QLc?qV#3{J z2NXp472qF*L$F?P z;=Fzy5EOl#A4vc3hS95gIo>VyDlyUGD%JN+-NLGzd31f^Y z0jF2F1NY#gb%`WI@r`O(TxFdsyDp~MXbw-Fcu6cvByQP|Vm`XaHM^FEFuewqTb#UW zs))_**sT%Jm_AI?(Md*U$t5oRkaNGQnK}~jX_E#nco4XMdh_MH5*8y<2S~vA3BNg> zK>jERE!b*!?Os)7<^Vws4@#a!^?UnV7=sbphcbk9&Cb;+hA4nBt;)6Lk`y^c(FYs7 zt_}<1?KheW!FhSgjP-|Y(s>oN)VxIF0QIJ^IoyCM=|*0Hx3xz3;?Jg5_v4 z72D>3)!xxar>>L+p}6A#)X}EsljMT6)+*1!_y&c`lXa7JE8}zs78*mDiipC}-<}S)0;btizf~LLEogT;qz?@E0KnJ3B~=roFi+XF*|wTJ(|Ec^{kM0~*_C z$Lr}M_!G`y6wlIY{7_=mvgq@JDJjfNiTqe8TT?3w!?rnu&NQIePkjbhm3 z-vm2yz}I5V$U7Ab-g-!Db25+^=tvXIA1%iv*LXYh6bTXxdYD|=d37n%>px1^`3H6M z>L=uPS=uQI`6W%G^7#A*2I*9UNR%VVZZi z$0qXJMi7%%7()Fn1c;J5g;y)TybUB6A6bUH4pg{9fSfTSXAK;2=5pnI_Bo}lN}o$y zNIG%cZ942O2?I9S&d zOb!z=$jI~=a|vV(cRA<@hbL-{?hqX+O9JH3;RN3m*ju&%UN?yr3C$9YR7w zqu~OZA0RoVyt{znoFe4ufhZHDfkwxUf>qNA%Mh-OuFkpyLUL-R@=TZ`qLl70=SjC@`M9&WS(wgKiNis+| zZ-^X}@q!Jv48kL^t=Zn@6NxoT51E3&Y?A(hnu9k#2c+o3i0#FMW45kCmS~5>Bf#Vs zU;iA(B8c41AGg#KY}ufS*5+P`&qGSTSW(3C7$IZikQ{H`Jox?1M5e N`BKZk#C4 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 testsFromConfigFile = null; + protected ArrayList testComparators = null; + protected String thisTestCaseName = null; + protected ComparatorData comparatorData = null; + protected Configuration conf = null; + protected String clitestDataDir = null; + protected String username = null; + + /** + * Read the test config file - testConfig.xml + */ + protected void readTestConfigFile() { + String testConfigFile = getTestFile(); + if (testsFromConfigFile == null) { + boolean success = false; + testConfigFile = TEST_CACHE_DATA_DIR + File.separator + testConfigFile; + try { + SAXParser p = (SAXParserFactory.newInstance()).newSAXParser(); + p.parse(testConfigFile, new TestConfigFileParser()); + success = true; + } catch (Exception e) { + LOG.info("File: " + testConfigFile + " not found"); + success = false; + } + assertTrue("Error reading test config file", success); + } + } + + protected String getTestFile() { + return "testConf.xml"; + } + + /* + * Setup + */ + public void setUp() throws Exception { + // Read the testConfig.xml file + readTestConfigFile(); + + conf = new Configuration(); + conf.setBoolean(ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, + true); + + clitestDataDir = new File(TEST_CACHE_DATA_DIR). + toURI().toString().replace(' ', '+'); + } + + /** + * Tear down + */ + public void tearDown() throws Exception { + displayResults(); + } + + /** + * Expand the commands from the test config xml file + * @param cmd + * @return String expanded command + */ + protected String expandCommand(final String cmd) { + String expCmd = cmd; + expCmd = expCmd.replaceAll("CLITEST_DATA", clitestDataDir); + expCmd = expCmd.replaceAll("USERNAME", username); + + return expCmd; + } + + /** + * Display the summarized results + */ + private void displayResults() { + LOG.info("Detailed results:"); + LOG.info("----------------------------------\n"); + + for (int i = 0; i < testsFromConfigFile.size(); i++) { + CLITestData td = testsFromConfigFile.get(i); + + boolean testResult = td.getTestResult(); + + // Display the details only if there is a failure + if (!testResult) { + LOG.info("-------------------------------------------"); + LOG.info(" Test ID: [" + (i + 1) + "]"); + LOG.info(" Test Description: [" + td.getTestDesc() + "]"); + LOG.info(""); + + ArrayList testCommands = td.getTestCommands(); + for (TestCmd cmd : testCommands) { + LOG.info(" Test Commands: [" + + expandCommand(cmd.getCmd()) + "]"); + } + + LOG.info(""); + ArrayList cleanupCommands = td.getCleanupCommands(); + for (TestCmd cmd : cleanupCommands) { + LOG.info(" Cleanup Commands: [" + + expandCommand(cmd.getCmd()) + "]"); + } + + LOG.info(""); + ArrayList compdata = td.getComparatorData(); + for (ComparatorData cd : compdata) { + boolean resultBoolean = cd.getTestResult(); + LOG.info(" Comparator: [" + + cd.getComparatorType() + "]"); + LOG.info(" Comparision result: [" + + (resultBoolean ? "pass" : "fail") + "]"); + LOG.info(" Expected output: [" + + cd.getExpectedOutput() + "]"); + LOG.info(" Actual output: [" + + cd.getActualOutput() + "]"); + } + LOG.info(""); + } + } + + LOG.info("Summary results:"); + LOG.info("----------------------------------\n"); + + boolean overallResults = true; + int totalPass = 0; + int totalFail = 0; + int totalComparators = 0; + for (int i = 0; i < testsFromConfigFile.size(); i++) { + CLITestData td = testsFromConfigFile.get(i); + totalComparators += + testsFromConfigFile.get(i).getComparatorData().size(); + boolean resultBoolean = td.getTestResult(); + if (resultBoolean) { + totalPass ++; + } else { + totalFail ++; + } + overallResults &= resultBoolean; + } + + + LOG.info(" Testing mode: " + testMode); + LOG.info(""); + LOG.info(" Overall result: " + + (overallResults ? "+++ PASS +++" : "--- FAIL ---")); + if ((totalPass + totalFail) == 0) { + LOG.info(" # Tests pass: " + 0); + LOG.info(" # Tests fail: " + 0); + } + else + { + LOG.info(" # Tests pass: " + totalPass + + " (" + (100 * totalPass / (totalPass + totalFail)) + "%)"); + LOG.info(" # Tests fail: " + totalFail + + " (" + (100 * totalFail / (totalPass + totalFail)) + "%)"); + } + + LOG.info(" # Validations done: " + totalComparators + + " (each test may do multiple validations)"); + + LOG.info(""); + LOG.info("Failing tests:"); + LOG.info("--------------"); + int i = 0; + boolean foundTests = false; + for (i = 0; i < testsFromConfigFile.size(); i++) { + boolean resultBoolean = testsFromConfigFile.get(i).getTestResult(); + if (!resultBoolean) { + LOG.info((i + 1) + ": " + + testsFromConfigFile.get(i).getTestDesc()); + foundTests = true; + } + } + if (!foundTests) { + LOG.info("NONE"); + } + + foundTests = false; + LOG.info(""); + LOG.info("Passing tests:"); + LOG.info("--------------"); + for (i = 0; i < testsFromConfigFile.size(); i++) { + boolean resultBoolean = testsFromConfigFile.get(i).getTestResult(); + if (resultBoolean) { + LOG.info((i + 1) + ": " + + testsFromConfigFile.get(i).getTestDesc()); + foundTests = true; + } + } + if (!foundTests) { + LOG.info("NONE"); + } + + assertTrue("One of the tests failed. " + + "See the Detailed results to identify " + + "the command that failed", overallResults); + + } + + /** + * Compare the actual output with the expected output + * @param compdata + * @return + */ + private boolean compareTestOutput(ComparatorData compdata, Result cmdResult) { + // Compare the output based on the comparator + String comparatorType = compdata.getComparatorType(); + Class comparatorClass = null; + + // If testMode is "test", then run the command and compare the output + // If testMode is "nocompare", then run the command and dump the output. + // Do not compare + + boolean compareOutput = false; + + if (testMode.equals(TESTMODE_TEST)) { + try { + // Initialize the comparator class and run its compare method + comparatorClass = Class.forName("org.apache.hadoop.cli.util." + + comparatorType); + ComparatorBase comp = (ComparatorBase) comparatorClass.newInstance(); + compareOutput = comp.compare(cmdResult.getCommandOutput(), + compdata.getExpectedOutput()); + } catch (Exception e) { + LOG.info("Error in instantiating the comparator" + e); + } + } + + return compareOutput; + } + + /*********************************** + ************* TESTS + *********************************/ + + public void testAll() { + LOG.info("TestAll"); + + // Run the tests defined in the testConf.xml config file. + for (int index = 0; index < testsFromConfigFile.size(); index++) { + + CLITestData testdata = (CLITestData) testsFromConfigFile.get(index); + + // Execute the test commands + ArrayList testCommands = testdata.getTestCommands(); + Result cmdResult = null; + for (TestCmd cmd : testCommands) { + try { + cmdResult = execute(cmd); + } catch (Exception e) { + fail(StringUtils.stringifyException(e)); + } + } + + boolean overallTCResult = true; + // Run comparators + ArrayList compdata = testdata.getComparatorData(); + for (ComparatorData cd : compdata) { + final String comptype = cd.getComparatorType(); + + boolean compareOutput = false; + + if (! comptype.equalsIgnoreCase("none")) { + compareOutput = compareTestOutput(cd, cmdResult); + overallTCResult &= compareOutput; + } + + cd.setExitCode(cmdResult.getExitCode()); + cd.setActualOutput(cmdResult.getCommandOutput()); + cd.setTestResult(compareOutput); + } + testdata.setTestResult(overallTCResult); + + // Execute the cleanup commands + ArrayList cleanupCommands = testdata.getCleanupCommands(); + for (TestCmd cmd : cleanupCommands) { + try { + execute(cmd); + } catch (Exception e) { + fail(StringUtils.stringifyException(e)); + } + } + } + } + + protected CommandExecutor.Result execute(TestCmd cmd) throws Exception { + throw new Exception("Unknow type of Test command:"+ cmd.getType()); + } + + /* + * Parser class for the test config xml file + */ + class TestConfigFileParser extends DefaultHandler { + String charString = null; + CLITestData td = null; + ArrayList testCommands = null; + ArrayList cleanupCommands = null; + + @Override + public void startDocument() throws SAXException { + testsFromConfigFile = new ArrayList(); + } + + @Override + public void startElement(String uri, + String localName, + String qName, + Attributes attributes) throws SAXException { + if (qName.equals("test")) { + td = new CLITestData(); + } else if (qName.equals("test-commands")) { + testCommands = new ArrayList(); + } else if (qName.equals("cleanup-commands")) { + cleanupCommands = new ArrayList(); + } else if (qName.equals("comparators")) { + testComparators = new ArrayList(); + } else if (qName.equals("comparator")) { + comparatorData = new ComparatorData(); + } + charString = ""; + } + + @Override + public void endElement(String uri, + String localName, + String qName) throws SAXException { + if (qName.equals("description")) { + td.setTestDesc(charString); + } else if (qName.equals("test-commands")) { + td.setTestCommands(testCommands); + testCommands = null; + } else if (qName.equals("cleanup-commands")) { + td.setCleanupCommands(cleanupCommands); + cleanupCommands = null; + } else if (qName.equals("command")) { + if (testCommands != null) { + testCommands.add(new TestCmd(charString, CommandType.FS)); + } else if (cleanupCommands != null) { + cleanupCommands.add(new TestCmd(charString, CommandType.FS)); + } + } else if (qName.equals("dfs-admin-command")) { + if (testCommands != null) { + testCommands.add(new TestCmd(charString, CommandType.DFSADMIN)); + } else if (cleanupCommands != null) { + cleanupCommands.add(new TestCmd(charString, CommandType.DFSADMIN)); + } + } else if (qName.equals("mr-admin-command")) { + if (testCommands != null) { + testCommands.add(new TestCmd(charString, CommandType.MRADMIN)); + } else if (cleanupCommands != null) { + cleanupCommands.add(new TestCmd(charString, CommandType.MRADMIN)); + } + } else if (qName.equals("archive-command")) { + if (testCommands != null) { + testCommands.add(new TestCmd(charString, CommandType.ARCHIVE)); + } else if (cleanupCommands != null) { + cleanupCommands.add(new TestCmd(charString, CommandType.ARCHIVE)); + } + } else if (qName.equals("comparators")) { + td.setComparatorData(testComparators); + } else if (qName.equals("comparator")) { + testComparators.add(comparatorData); + } else if (qName.equals("type")) { + comparatorData.setComparatorType(charString); + } else if (qName.equals("expected-output")) { + comparatorData.setExpectedOutput(charString); + } else if (qName.equals("test")) { + testsFromConfigFile.add(td); + td = null; + } else if (qName.equals("mode")) { + testMode = charString; + if (!testMode.equals(TESTMODE_NOCOMPARE) && + !testMode.equals(TESTMODE_TEST)) { + testMode = TESTMODE_TEST; + } + } + } + + @Override + public void characters(char[] ch, + int start, + int length) throws SAXException { + String s = new String(ch, start, length); + charString += s; + } + } +} diff --git a/src/test/org/apache/hadoop/cli/testConf.xml b/src/test/org/apache/hadoop/cli/testConf.xml new file mode 100644 index 00000000000..3250aa67a18 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/testConf.xml @@ -0,0 +1,18 @@ + + + + + + test + + + + + + diff --git a/src/test/org/apache/hadoop/cli/testConf.xsl b/src/test/org/apache/hadoop/cli/testConf.xsl new file mode 100644 index 00000000000..09fb0b7a500 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/testConf.xsl @@ -0,0 +1,28 @@ + + + + + + + +

Hadoop DFS command-line tests

+ + + + + + + + + + + + + + +
IDCommandDescription
+ + + + + diff --git a/src/test/org/apache/hadoop/cli/util/CLITestData.java b/src/test/org/apache/hadoop/cli/util/CLITestData.java new file mode 100644 index 00000000000..18a7133218f --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/CLITestData.java @@ -0,0 +1,136 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +import java.util.ArrayList; + +/** + * + * Class to store CLI Test Data + */ +public class CLITestData { + private String testDesc = null; + private ArrayList testCommands = null; + private ArrayList cleanupCommands = null; + private ArrayList comparatorData = null; + private boolean testResult = false; + + public CLITestData() { + + } + + /** + * Class to define Test Command. includes type of the command and command itself + * Valid types FS, DFSADMIN, MRADMIN and ARCHIVE. + */ + static public class TestCmd { + public enum CommandType { + FS, + DFSADMIN, + MRADMIN, + ARCHIVE + } + private final CommandType type; + private final String cmd; + + public TestCmd(String str, CommandType type) { + cmd = str; + this.type = type; + } + public CommandType getType() { + return type; + } + public String getCmd() { + return cmd; + } + public String toString() { + return cmd; + } + } + + /** + * @return the testDesc + */ + public String getTestDesc() { + return testDesc; + } + + /** + * @param testDesc the testDesc to set + */ + public void setTestDesc(String testDesc) { + this.testDesc = testDesc; + } + + /** + * @return the testCommands + */ + public ArrayList getTestCommands() { + return testCommands; + } + + /** + * @param testCommands the testCommands to set + */ + public void setTestCommands(ArrayList testCommands) { + this.testCommands = testCommands; + } + + /** + * @return the comparatorData + */ + public ArrayList getComparatorData() { + return comparatorData; + } + + /** + * @param comparatorData the comparatorData to set + */ + public void setComparatorData(ArrayList comparatorData) { + this.comparatorData = comparatorData; + } + + /** + * @return the testResult + */ + public boolean getTestResult() { + return testResult; + } + + /** + * @param testResult the testResult to set + */ + public void setTestResult(boolean testResult) { + this.testResult = testResult; + } + + /** + * @return the cleanupCommands + */ + public ArrayList getCleanupCommands() { + return cleanupCommands; + } + + /** + * @param cleanupCommands the cleanupCommands to set + */ + public void setCleanupCommands(ArrayList cleanupCommands) { + this.cleanupCommands = cleanupCommands; + } +} diff --git a/src/test/org/apache/hadoop/cli/util/CommandExecutor.java b/src/test/org/apache/hadoop/cli/util/CommandExecutor.java new file mode 100644 index 00000000000..7a0dc462a06 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/CommandExecutor.java @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.util.StringTokenizer; + +import org.apache.hadoop.cli.TestCLI; + +/** + * + * This class execute commands and captures the output + */ +public abstract class CommandExecutor { + protected String[] getCommandAsArgs(final String cmd, final String masterKey, + final String master) { + StringTokenizer tokenizer = new StringTokenizer(cmd, " "); + String[] args = new String[tokenizer.countTokens()]; + + int i = 0; + while (tokenizer.hasMoreTokens()) { + args[i] = tokenizer.nextToken(); + + args[i] = args[i].replaceAll(masterKey, master); + args[i] = args[i].replaceAll("CLITEST_DATA", + new File(TestCLI.TEST_CACHE_DATA_DIR). + toURI().toString().replace(' ', '+')); + args[i] = args[i].replaceAll("USERNAME", System.getProperty("user.name")); + + i++; + } + + return args; + } + + public Result executeCommand(final String cmd) throws Exception { + int exitCode = 0; + Exception lastException = null; + + + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + PrintStream origOut = System.out; + PrintStream origErr = System.err; + + System.setOut(new PrintStream(bao)); + System.setErr(new PrintStream(bao)); + + try { + execute(cmd); + } catch (Exception e) { + e.printStackTrace(); + lastException = e; + exitCode = -1; + } finally { + System.setOut(origOut); + System.setErr(origErr); + } + return new Result(bao.toString(), exitCode, lastException, cmd); + } + + protected abstract void execute(final String cmd) throws Exception; + + public static class Result { + final String commandOutput; + final int exitCode; + final Exception exception; + final String cmdExecuted; + public Result(String commandOutput, int exitCode, Exception exception, + String cmdExecuted) { + this.commandOutput = commandOutput; + this.exitCode = exitCode; + this.exception = exception; + this.cmdExecuted = cmdExecuted; + } + + public String getCommandOutput() { + return commandOutput; + } + + public int getExitCode() { + return exitCode; + } + + public Exception getException() { + return exception; + } + + public String getCommand() { + return cmdExecuted; + } + } + +} diff --git a/src/test/org/apache/hadoop/cli/util/ComparatorBase.java b/src/test/org/apache/hadoop/cli/util/ComparatorBase.java new file mode 100644 index 00000000000..fae99377a42 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/ComparatorBase.java @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +/** + * + * Comparator interface. To define a new comparator, implement the compare + * method + */ +public abstract class ComparatorBase { + public ComparatorBase() { + + } + + /** + * Compare method for the comparator class. + * @param actual output. can be null + * @param expected output. can be null + * @return true if expected output compares with the actual output, else + * return false. If actual or expected is null, return false + */ + public abstract boolean compare(String actual, String expected); +} diff --git a/src/test/org/apache/hadoop/cli/util/ComparatorData.java b/src/test/org/apache/hadoop/cli/util/ComparatorData.java new file mode 100644 index 00000000000..1b24777e4c5 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/ComparatorData.java @@ -0,0 +1,106 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +/** + * + * Class to store CLI Test Comparators Data + */ +public class ComparatorData { + private String expectedOutput = null; + private String actualOutput = null; + private boolean testResult = false; + private int exitCode = 0; + private String comparatorType = null; + + public ComparatorData() { + + } + + /** + * @return the expectedOutput + */ + public String getExpectedOutput() { + return expectedOutput; + } + + /** + * @param expectedOutput the expectedOutput to set + */ + public void setExpectedOutput(String expectedOutput) { + this.expectedOutput = expectedOutput; + } + + /** + * @return the actualOutput + */ + public String getActualOutput() { + return actualOutput; + } + + /** + * @param actualOutput the actualOutput to set + */ + public void setActualOutput(String actualOutput) { + this.actualOutput = actualOutput; + } + + /** + * @return the testResult + */ + public boolean getTestResult() { + return testResult; + } + + /** + * @param testResult the testResult to set + */ + public void setTestResult(boolean testResult) { + this.testResult = testResult; + } + + /** + * @return the exitCode + */ + public int getExitCode() { + return exitCode; + } + + /** + * @param exitCode the exitCode to set + */ + public void setExitCode(int exitCode) { + this.exitCode = exitCode; + } + + /** + * @return the comparatorType + */ + public String getComparatorType() { + return comparatorType; + } + + /** + * @param comparatorType the comparatorType to set + */ + public void setComparatorType(String comparatorType) { + this.comparatorType = comparatorType; + } + +} diff --git a/src/test/org/apache/hadoop/cli/util/ExactComparator.java b/src/test/org/apache/hadoop/cli/util/ExactComparator.java new file mode 100644 index 00000000000..9a49a960ce0 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/ExactComparator.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +/** + * Comparator for the Command line tests. + * + * This comparator compares the actual to the expected and + * returns true only if they are the same + * + */ +public class ExactComparator extends ComparatorBase { + + @Override + public boolean compare(String actual, String expected) { + return actual.equals(expected); + } +} diff --git a/src/test/org/apache/hadoop/cli/util/RegexpAcrossOutputComparator.java b/src/test/org/apache/hadoop/cli/util/RegexpAcrossOutputComparator.java new file mode 100644 index 00000000000..9285bde9454 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/RegexpAcrossOutputComparator.java @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +import java.util.regex.Pattern; + +/** + * Comparator for command line tests that attempts to find a regexp + * within the entire text returned by a command. + * + * This comparator differs from RegexpComparator in that it attempts + * to match the pattern within all of the text returned by the command, + * rather than matching against each line of the returned text. This + * allows matching against patterns that span multiple lines. + */ +public class RegexpAcrossOutputComparator extends ComparatorBase { + + @Override + public boolean compare(String actual, String expected) { + return Pattern.compile(expected).matcher(actual).find(); + } + +} diff --git a/src/test/org/apache/hadoop/cli/util/RegexpComparator.java b/src/test/org/apache/hadoop/cli/util/RegexpComparator.java new file mode 100644 index 00000000000..f2477466c12 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/RegexpComparator.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Comparator for the Command line tests. + * + * This comparator searches for the regular expression specified in 'expected' + * in the string 'actual' and returns true if the regular expression match is + * done + * + */ +public class RegexpComparator extends ComparatorBase { + + @Override + public boolean compare(String actual, String expected) { + boolean success = false; + Pattern p = Pattern.compile(expected); + + StringTokenizer tokenizer = new StringTokenizer(actual, "\n\r"); + while (tokenizer.hasMoreTokens() && !success) { + String actualToken = tokenizer.nextToken(); + Matcher m = p.matcher(actualToken); + success = m.matches(); + } + + return success; + } + +} diff --git a/src/test/org/apache/hadoop/cli/util/SubstringComparator.java b/src/test/org/apache/hadoop/cli/util/SubstringComparator.java new file mode 100644 index 00000000000..79e9a889fd8 --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/SubstringComparator.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +public class SubstringComparator extends ComparatorBase { + + @Override + public boolean compare(String actual, String expected) { + int compareOutput = actual.indexOf(expected); + if (compareOutput == -1) { + return false; + } + + return true; + } + +} diff --git a/src/test/org/apache/hadoop/cli/util/TokenComparator.java b/src/test/org/apache/hadoop/cli/util/TokenComparator.java new file mode 100644 index 00000000000..ce5b8468c5b --- /dev/null +++ b/src/test/org/apache/hadoop/cli/util/TokenComparator.java @@ -0,0 +1,49 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.cli.util; + +import java.util.StringTokenizer; + +/** + * Comparator for the Command line tests. + * + * This comparator compares each token in the expected output and returns true + * if all tokens are in the actual output + * + */ +public class TokenComparator extends ComparatorBase { + + @Override + public boolean compare(String actual, String expected) { + boolean compareOutput = true; + + StringTokenizer tokenizer = new StringTokenizer(expected, ",\n\r"); + + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (actual.indexOf(token) != -1) { + compareOutput &= true; + } else { + compareOutput &= false; + } + } + + return compareOutput; + } +} diff --git a/src/test/org/apache/hadoop/conf/TestConfiguration.java b/src/test/org/apache/hadoop/conf/TestConfiguration.java new file mode 100644 index 00000000000..e509fd34641 --- /dev/null +++ b/src/test/org/apache/hadoop/conf/TestConfiguration.java @@ -0,0 +1,392 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.conf; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.util.ArrayList; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.hadoop.fs.Path; + + +public class TestConfiguration extends TestCase { + + private Configuration conf; + final static String CONFIG = new File("./test-config.xml").getAbsolutePath(); + final static String CONFIG2 = new File("./test-config2.xml").getAbsolutePath(); + final static Random RAN = new Random(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + conf = new Configuration(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + new File(CONFIG).delete(); + new File(CONFIG2).delete(); + } + + private void startConfig() throws IOException{ + out.write("\n"); + out.write("\n"); + } + + private void endConfig() throws IOException{ + out.write("\n"); + out.close(); + } + + private void addInclude(String filename) throws IOException{ + out.write("\n "); + } + + public void testVariableSubstitution() throws IOException { + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + declareProperty("my.int", "${intvar}", "42"); + declareProperty("intvar", "42", "42"); + declareProperty("my.base", "/tmp/${user.name}", UNSPEC); + declareProperty("my.file", "hello", "hello"); + declareProperty("my.suffix", ".txt", ".txt"); + declareProperty("my.relfile", "${my.file}${my.suffix}", "hello.txt"); + declareProperty("my.fullfile", "${my.base}/${my.file}${my.suffix}", UNSPEC); + // check that undefined variables are returned as-is + declareProperty("my.failsexpand", "a${my.undefvar}b", "a${my.undefvar}b"); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + + for (Prop p : props) { + System.out.println("p=" + p.name); + String gotVal = conf.get(p.name); + String gotRawVal = conf.getRaw(p.name); + assertEq(p.val, gotRawVal); + if (p.expectEval == UNSPEC) { + // expansion is system-dependent (uses System properties) + // can't do exact match so just check that all variables got expanded + assertTrue(gotVal != null && -1 == gotVal.indexOf("${")); + } else { + assertEq(p.expectEval, gotVal); + } + } + + // check that expansion also occurs for getInt() + assertTrue(conf.getInt("intvar", -1) == 42); + assertTrue(conf.getInt("my.int", -1) == 42); + } + + public static void assertEq(Object a, Object b) { + System.out.println("assertEq: " + a + ", " + b); + assertEquals(a, b); + } + + static class Prop { + String name; + String val; + String expectEval; + } + + final String UNSPEC = null; + ArrayList props = new ArrayList(); + + void declareProperty(String name, String val, String expectEval) + throws IOException { + declareProperty(name, val, expectEval, false); + } + + void declareProperty(String name, String val, String expectEval, + boolean isFinal) + throws IOException { + appendProperty(name, val, isFinal); + Prop p = new Prop(); + p.name = name; + p.val = val; + p.expectEval = expectEval; + props.add(p); + } + + void appendProperty(String name, String val) throws IOException { + appendProperty(name, val, false); + } + + void appendProperty(String name, String val, boolean isFinal) + throws IOException { + out.write(""); + out.write(""); + out.write(name); + out.write(""); + out.write(""); + out.write(val); + out.write(""); + if (isFinal) { + out.write("true"); + } + out.write("\n"); + } + + public void testOverlay() throws IOException{ + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("a","b"); + appendProperty("b","c"); + appendProperty("d","e"); + appendProperty("e","f", true); + endConfig(); + + out=new BufferedWriter(new FileWriter(CONFIG2)); + startConfig(); + appendProperty("a","b"); + appendProperty("b","d"); + appendProperty("e","e"); + endConfig(); + + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + + //set dynamically something + conf.set("c","d"); + conf.set("a","d"); + + Configuration clone=new Configuration(conf); + clone.addResource(new Path(CONFIG2)); + + assertEquals(clone.get("a"), "d"); + assertEquals(clone.get("b"), "d"); + assertEquals(clone.get("c"), "d"); + assertEquals(clone.get("d"), "e"); + assertEquals(clone.get("e"), "f"); + + } + + public void testCommentsInValue() throws IOException { + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("my.comment", "this contains a comment"); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + //two spaces one after "this", one before "contains" + assertEquals("this contains a comment", conf.get("my.comment")); + } + + public void testTrim() throws IOException { + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + String[] whitespaces = {"", " ", "\n", "\t"}; + String[] name = new String[100]; + for(int i = 0; i < name.length; i++) { + name[i] = "foo" + i; + StringBuilder prefix = new StringBuilder(); + StringBuilder postfix = new StringBuilder(); + for(int j = 0; j < 3; j++) { + prefix.append(whitespaces[RAN.nextInt(whitespaces.length)]); + postfix.append(whitespaces[RAN.nextInt(whitespaces.length)]); + } + + appendProperty(prefix + name[i] + postfix, name[i] + ".value"); + } + endConfig(); + + conf.addResource(new Path(CONFIG)); + for(String n : name) { + assertEquals(n + ".value", conf.get(n)); + } + } + + public void testToString() throws IOException { + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + + String expectedOutput = + "Configuration: core-default.xml, core-site.xml, " + + fileResource.toString(); + assertEquals(expectedOutput, conf.toString()); + } + + public void testIncludes() throws Exception { + tearDown(); + System.out.println("XXX testIncludes"); + out=new BufferedWriter(new FileWriter(CONFIG2)); + startConfig(); + appendProperty("a","b"); + appendProperty("c","d"); + endConfig(); + + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + addInclude(CONFIG2); + appendProperty("e","f"); + appendProperty("g","h"); + endConfig(); + + // verify that the includes file contains all properties + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + assertEquals(conf.get("a"), "b"); + assertEquals(conf.get("c"), "d"); + assertEquals(conf.get("e"), "f"); + assertEquals(conf.get("g"), "h"); + tearDown(); + } + + BufferedWriter out; + + public void testIntegerRanges() { + Configuration conf = new Configuration(); + conf.set("first", "-100"); + conf.set("second", "4-6,9-10,27"); + conf.set("third", "34-"); + Configuration.IntegerRanges range = conf.getRange("first", null); + System.out.println("first = " + range); + assertEquals(true, range.isIncluded(0)); + assertEquals(true, range.isIncluded(1)); + assertEquals(true, range.isIncluded(100)); + assertEquals(false, range.isIncluded(101)); + range = conf.getRange("second", null); + System.out.println("second = " + range); + assertEquals(false, range.isIncluded(3)); + assertEquals(true, range.isIncluded(4)); + assertEquals(true, range.isIncluded(6)); + assertEquals(false, range.isIncluded(7)); + assertEquals(false, range.isIncluded(8)); + assertEquals(true, range.isIncluded(9)); + assertEquals(true, range.isIncluded(10)); + assertEquals(false, range.isIncluded(11)); + assertEquals(false, range.isIncluded(26)); + assertEquals(true, range.isIncluded(27)); + assertEquals(false, range.isIncluded(28)); + range = conf.getRange("third", null); + System.out.println("third = " + range); + assertEquals(false, range.isIncluded(33)); + assertEquals(true, range.isIncluded(34)); + assertEquals(true, range.isIncluded(100000000)); + } + + public void testHexValues() throws IOException{ + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("test.hex1", "0x10"); + appendProperty("test.hex2", "0xF"); + appendProperty("test.hex3", "-0x10"); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + assertEquals(16, conf.getInt("test.hex1", 0)); + assertEquals(16, conf.getLong("test.hex1", 0)); + assertEquals(15, conf.getInt("test.hex2", 0)); + assertEquals(15, conf.getLong("test.hex2", 0)); + assertEquals(-16, conf.getInt("test.hex3", 0)); + assertEquals(-16, conf.getLong("test.hex3", 0)); + + } + + public void testIntegerValues() throws IOException{ + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("test.int1", "20"); + appendProperty("test.int2", "020"); + appendProperty("test.int3", "-20"); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + assertEquals(20, conf.getInt("test.int1", 0)); + assertEquals(20, conf.getLong("test.int1", 0)); + assertEquals(20, conf.getInt("test.int2", 0)); + assertEquals(20, conf.getLong("test.int2", 0)); + assertEquals(-20, conf.getInt("test.int3", 0)); + assertEquals(-20, conf.getLong("test.int3", 0)); + } + + public void testReload() throws IOException { + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("test.key1", "final-value1", true); + appendProperty("test.key2", "value2"); + endConfig(); + Path fileResource = new Path(CONFIG); + conf.addResource(fileResource); + + out=new BufferedWriter(new FileWriter(CONFIG2)); + startConfig(); + appendProperty("test.key1", "value1"); + appendProperty("test.key3", "value3"); + endConfig(); + Path fileResource1 = new Path(CONFIG2); + conf.addResource(fileResource1); + + // add a few values via set. + conf.set("test.key3", "value4"); + conf.set("test.key4", "value5"); + + assertEquals("final-value1", conf.get("test.key1")); + assertEquals("value2", conf.get("test.key2")); + assertEquals("value4", conf.get("test.key3")); + assertEquals("value5", conf.get("test.key4")); + + // change values in the test file... + out=new BufferedWriter(new FileWriter(CONFIG)); + startConfig(); + appendProperty("test.key1", "final-value1"); + appendProperty("test.key3", "final-value3", true); + endConfig(); + + conf.reloadConfiguration(); + assertEquals("value1", conf.get("test.key1")); + // overlayed property overrides. + assertEquals("value4", conf.get("test.key3")); + assertEquals(null, conf.get("test.key2")); + assertEquals("value5", conf.get("test.key4")); + } + + public void testSize() throws IOException { + Configuration conf = new Configuration(false); + conf.set("a", "A"); + conf.set("b", "B"); + assertEquals(2, conf.size()); + } + + public void testClear() throws IOException { + Configuration conf = new Configuration(false); + conf.set("a", "A"); + conf.set("b", "B"); + conf.clear(); + assertEquals(0, conf.size()); + assertFalse(conf.iterator().hasNext()); + } + + public static void main(String[] argv) throws Exception { + junit.textui.TestRunner.main(new String[]{ + TestConfiguration.class.getName() + }); + } +} diff --git a/src/test/org/apache/hadoop/conf/TestConfigurationSubclass.java b/src/test/org/apache/hadoop/conf/TestConfigurationSubclass.java new file mode 100644 index 00000000000..fd2fa38967e --- /dev/null +++ b/src/test/org/apache/hadoop/conf/TestConfigurationSubclass.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.conf; + +import junit.framework.TestCase; + +import java.util.Properties; + +/** + * Created 21-Jan-2009 13:42:36 + */ + +public class TestConfigurationSubclass extends TestCase { + private static final String EMPTY_CONFIGURATION_XML + = "/org/apache/hadoop/conf/empty-configuration.xml"; + + + public void testGetProps() { + SubConf conf = new SubConf(true); + Properties properties = conf.getProperties(); + assertNotNull("hadoop.tmp.dir is not set", + properties.getProperty("hadoop.tmp.dir")); + } + + public void testReload() throws Throwable { + SubConf conf = new SubConf(true); + assertFalse(conf.isReloaded()); + Configuration.addDefaultResource(EMPTY_CONFIGURATION_XML); + assertTrue(conf.isReloaded()); + Properties properties = conf.getProperties(); + } + + public void testReloadNotQuiet() throws Throwable { + SubConf conf = new SubConf(true); + conf.setQuietMode(false); + assertFalse(conf.isReloaded()); + conf.addResource("not-a-valid-resource"); + assertTrue(conf.isReloaded()); + try { + Properties properties = conf.getProperties(); + fail("Should not have got here"); + } catch (RuntimeException e) { + assertTrue(e.toString(),e.getMessage().contains("not found")); + } + } + + private static class SubConf extends Configuration { + + private boolean reloaded; + + /** + * A new configuration where the behavior of reading from the default resources + * can be turned off. + * + * If the parameter {@code loadDefaults} is false, the new instance will not + * load resources from the default files. + * + * @param loadDefaults specifies whether to load from the default files + */ + private SubConf(boolean loadDefaults) { + super(loadDefaults); + } + + public Properties getProperties() { + return super.getProps(); + } + + /** + * {@inheritDoc}. + * Sets the reloaded flag. + */ + @Override + public void reloadConfiguration() { + super.reloadConfiguration(); + reloaded = true; + } + + public boolean isReloaded() { + return reloaded; + } + + public void setReloaded(boolean reloaded) { + this.reloaded = reloaded; + } + } + +} diff --git a/src/test/org/apache/hadoop/conf/TestGetInstances.java b/src/test/org/apache/hadoop/conf/TestGetInstances.java new file mode 100644 index 00000000000..57b7ff45198 --- /dev/null +++ b/src/test/org/apache/hadoop/conf/TestGetInstances.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.conf; + +import java.util.List; + +import junit.framework.TestCase; + +public class TestGetInstances extends TestCase { + + interface SampleInterface {} + + interface ChildInterface extends SampleInterface {} + + static class SampleClass implements SampleInterface { + SampleClass() {} + } + + static class AnotherClass implements ChildInterface { + AnotherClass() {} + } + + /** + * Makes sure Configuration.getInstances() returns + * instances of the required type. + */ + public void testGetInstances() throws Exception { + Configuration conf = new Configuration(); + + List classes = + conf.getInstances("no.such.property", SampleInterface.class); + assertTrue(classes.isEmpty()); + + conf.set("empty.property", ""); + classes = conf.getInstances("empty.property", SampleInterface.class); + assertTrue(classes.isEmpty()); + + conf.setStrings("some.classes", + SampleClass.class.getName(), AnotherClass.class.getName()); + classes = conf.getInstances("some.classes", SampleInterface.class); + assertEquals(2, classes.size()); + + try { + conf.setStrings("some.classes", + SampleClass.class.getName(), AnotherClass.class.getName(), + String.class.getName()); + conf.getInstances("some.classes", SampleInterface.class); + fail("java.lang.String does not implement SampleInterface"); + } catch (RuntimeException e) {} + + try { + conf.setStrings("some.classes", + SampleClass.class.getName(), AnotherClass.class.getName(), + "no.such.Class"); + conf.getInstances("some.classes", SampleInterface.class); + fail("no.such.Class does not exist"); + } catch (RuntimeException e) {} + } +} diff --git a/src/test/org/apache/hadoop/conf/empty-configuration.xml b/src/test/org/apache/hadoop/conf/empty-configuration.xml new file mode 100644 index 00000000000..a2086fa683f --- /dev/null +++ b/src/test/org/apache/hadoop/conf/empty-configuration.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/test/org/apache/hadoop/filecache/TestDistributedCache.java b/src/test/org/apache/hadoop/filecache/TestDistributedCache.java new file mode 100644 index 00000000000..2da7f0bc145 --- /dev/null +++ b/src/test/org/apache/hadoop/filecache/TestDistributedCache.java @@ -0,0 +1,77 @@ +package org.apache.hadoop.filecache; + +import java.io.IOException; +import java.net.URI; +import java.util.Random; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +import junit.framework.TestCase; + +public class TestDistributedCache extends TestCase { + + static final URI LOCAL_FS = URI.create("file:///"); + private static String TEST_CACHE_BASE_DIR = + new Path(System.getProperty("test.build.data","/tmp/cachebasedir")) + .toString().replace(' ', '+'); + private static String TEST_ROOT_DIR = + System.getProperty("test.build.data", "/tmp/distributedcache"); + private static final int TEST_FILE_SIZE = 4 * 1024; // 4K + private static final int LOCAL_CACHE_LIMIT = 5 * 1024; //5K + private Configuration conf; + private Path firstCacheFile; + private Path secondCacheFile; + private FileSystem localfs; + + /** + * @see TestCase#setUp() + */ + @Override + protected void setUp() throws IOException { + conf = new Configuration(); + conf.setLong("local.cache.size", LOCAL_CACHE_LIMIT); + localfs = FileSystem.get(LOCAL_FS, conf); + firstCacheFile = new Path(TEST_ROOT_DIR+"/firstcachefile"); + secondCacheFile = new Path(TEST_ROOT_DIR+"/secondcachefile"); + createTempFile(localfs, firstCacheFile); + createTempFile(localfs, secondCacheFile); + } + + /** test delete cache */ + public void testDeleteCache() throws Exception { + DistributedCache.getLocalCache(firstCacheFile.toUri(), conf, new Path(TEST_CACHE_BASE_DIR), + false, System.currentTimeMillis(), new Path(TEST_ROOT_DIR)); + DistributedCache.releaseCache(firstCacheFile.toUri(), conf); + //in above code,localized a file of size 4K and then release the cache which will cause the cache + //be deleted when the limit goes out. The below code localize another cache which's designed to + //sweep away the first cache. + DistributedCache.getLocalCache(secondCacheFile.toUri(), conf, new Path(TEST_CACHE_BASE_DIR), + false, System.currentTimeMillis(), new Path(TEST_ROOT_DIR)); + FileStatus[] dirStatuses = localfs.listStatus(new Path(TEST_CACHE_BASE_DIR)); + assertTrue("DistributedCache failed deleting old cache when the cache store is full.", + dirStatuses.length > 1); + } + + private void createTempFile(FileSystem fs, Path p) throws IOException { + FSDataOutputStream out = fs.create(p); + byte[] toWrite = new byte[TEST_FILE_SIZE]; + new Random().nextBytes(toWrite); + out.write(toWrite); + out.close(); + FileSystem.LOG.info("created: " + p + ", size=" + TEST_FILE_SIZE); + } + + /** + * @see TestCase#tearDown() + */ + @Override + protected void tearDown() throws IOException { + localfs.delete(firstCacheFile, true); + localfs.delete(secondCacheFile, true); + localfs.close(); + } +} diff --git a/src/test/org/apache/hadoop/fs/FileSystemContractBaseTest.java b/src/test/org/apache/hadoop/fs/FileSystemContractBaseTest.java new file mode 100644 index 00000000000..8bdeb3bfd7d --- /dev/null +++ b/src/test/org/apache/hadoop/fs/FileSystemContractBaseTest.java @@ -0,0 +1,471 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +/** + *

+ * A collection of tests for the contract of the {@link FileSystem}. + * This test should be used for general-purpose implementations of + * {@link FileSystem}, that is, implementations that provide implementations + * of all of the functionality of {@link FileSystem}. + *

+ *

+ * To test a given {@link FileSystem} implementation create a subclass of this + * test and override {@link #setUp()} to initialize the fs + * {@link FileSystem} instance variable. + *

+ */ +public abstract class FileSystemContractBaseTest extends TestCase { + + protected FileSystem fs; + private byte[] data = new byte[getBlockSize() * 2]; // two blocks of data + { + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (i % 10); + } + } + + @Override + protected void tearDown() throws Exception { + fs.delete(path("/test"), true); + } + + protected int getBlockSize() { + return 1024; + } + + protected String getDefaultWorkingDirectory() { + return "/user/" + System.getProperty("user.name"); + } + + protected boolean renameSupported() { + return true; + } + + public void testFsStatus() throws Exception { + FsStatus fsStatus = fs.getStatus(); + assertNotNull(fsStatus); + //used, free and capacity are non-negative longs + assertTrue(fsStatus.getUsed() >= 0); + assertTrue(fsStatus.getRemaining() >= 0); + assertTrue(fsStatus.getCapacity() >= 0); + } + + public void testWorkingDirectory() throws Exception { + + Path workDir = path(getDefaultWorkingDirectory()); + assertEquals(workDir, fs.getWorkingDirectory()); + + fs.setWorkingDirectory(path(".")); + assertEquals(workDir, fs.getWorkingDirectory()); + + fs.setWorkingDirectory(path("..")); + assertEquals(workDir.getParent(), fs.getWorkingDirectory()); + + Path relativeDir = path("hadoop"); + fs.setWorkingDirectory(relativeDir); + assertEquals(relativeDir, fs.getWorkingDirectory()); + + Path absoluteDir = path("/test/hadoop"); + fs.setWorkingDirectory(absoluteDir); + assertEquals(absoluteDir, fs.getWorkingDirectory()); + + } + + public void testMkdirs() throws Exception { + Path testDir = path("/test/hadoop"); + assertFalse(fs.exists(testDir)); + assertFalse(fs.isFile(testDir)); + + assertTrue(fs.mkdirs(testDir)); + + assertTrue(fs.exists(testDir)); + assertFalse(fs.isFile(testDir)); + + assertTrue(fs.mkdirs(testDir)); + + assertTrue(fs.exists(testDir)); + assertFalse(fs.isFile(testDir)); + + Path parentDir = testDir.getParent(); + assertTrue(fs.exists(parentDir)); + assertFalse(fs.isFile(parentDir)); + + Path grandparentDir = parentDir.getParent(); + assertTrue(fs.exists(grandparentDir)); + assertFalse(fs.isFile(grandparentDir)); + + } + + public void testMkdirsFailsForSubdirectoryOfExistingFile() throws Exception { + Path testDir = path("/test/hadoop"); + assertFalse(fs.exists(testDir)); + assertTrue(fs.mkdirs(testDir)); + assertTrue(fs.exists(testDir)); + + createFile(path("/test/hadoop/file")); + + Path testSubDir = path("/test/hadoop/file/subdir"); + try { + fs.mkdirs(testSubDir); + fail("Should throw IOException."); + } catch (IOException e) { + // expected + } + assertFalse(fs.exists(testSubDir)); + + Path testDeepSubDir = path("/test/hadoop/file/deep/sub/dir"); + try { + fs.mkdirs(testDeepSubDir); + fail("Should throw IOException."); + } catch (IOException e) { + // expected + } + assertFalse(fs.exists(testDeepSubDir)); + + } + + public void testGetFileStatusThrowsExceptionForNonExistentFile() + throws Exception { + try { + fs.getFileStatus(path("/test/hadoop/file")); + fail("Should throw FileNotFoundException"); + } catch (FileNotFoundException e) { + // expected + } + } + + public void testListStatusReturnsNullForNonExistentFile() throws Exception { + assertNull(fs.listStatus(path("/test/hadoop/file"))); + } + + public void testListStatus() throws Exception { + Path[] testDirs = { path("/test/hadoop/a"), + path("/test/hadoop/b"), + path("/test/hadoop/c/1"), }; + assertFalse(fs.exists(testDirs[0])); + + for (Path path : testDirs) { + assertTrue(fs.mkdirs(path)); + } + + FileStatus[] paths = fs.listStatus(path("/test")); + assertEquals(1, paths.length); + assertEquals(path("/test/hadoop"), paths[0].getPath()); + + paths = fs.listStatus(path("/test/hadoop")); + assertEquals(3, paths.length); + assertEquals(path("/test/hadoop/a"), paths[0].getPath()); + assertEquals(path("/test/hadoop/b"), paths[1].getPath()); + assertEquals(path("/test/hadoop/c"), paths[2].getPath()); + + paths = fs.listStatus(path("/test/hadoop/a")); + assertEquals(0, paths.length); + } + + public void testWriteReadAndDeleteEmptyFile() throws Exception { + writeReadAndDelete(0); + } + + public void testWriteReadAndDeleteHalfABlock() throws Exception { + writeReadAndDelete(getBlockSize() / 2); + } + + public void testWriteReadAndDeleteOneBlock() throws Exception { + writeReadAndDelete(getBlockSize()); + } + + public void testWriteReadAndDeleteOneAndAHalfBlocks() throws Exception { + writeReadAndDelete(getBlockSize() + (getBlockSize() / 2)); + } + + public void testWriteReadAndDeleteTwoBlocks() throws Exception { + writeReadAndDelete(getBlockSize() * 2); + } + + private void writeReadAndDelete(int len) throws IOException { + Path path = path("/test/hadoop/file"); + + fs.mkdirs(path.getParent()); + + FSDataOutputStream out = fs.create(path, false, + fs.getConf().getInt("io.file.buffer.size", 4096), + (short) 1, getBlockSize()); + out.write(data, 0, len); + out.close(); + + assertTrue("Exists", fs.exists(path)); + assertEquals("Length", len, fs.getFileStatus(path).getLen()); + + FSDataInputStream in = fs.open(path); + byte[] buf = new byte[len]; + in.readFully(0, buf); + in.close(); + + assertEquals(len, buf.length); + for (int i = 0; i < buf.length; i++) { + assertEquals("Position " + i, data[i], buf[i]); + } + + assertTrue("Deleted", fs.delete(path, false)); + + assertFalse("No longer exists", fs.exists(path)); + + } + + public void testOverwrite() throws IOException { + Path path = path("/test/hadoop/file"); + + fs.mkdirs(path.getParent()); + + createFile(path); + + assertTrue("Exists", fs.exists(path)); + assertEquals("Length", data.length, fs.getFileStatus(path).getLen()); + + try { + fs.create(path, false); + fail("Should throw IOException."); + } catch (IOException e) { + // Expected + } + + FSDataOutputStream out = fs.create(path, true); + out.write(data, 0, data.length); + out.close(); + + assertTrue("Exists", fs.exists(path)); + assertEquals("Length", data.length, fs.getFileStatus(path).getLen()); + + } + + public void testWriteInNonExistentDirectory() throws IOException { + Path path = path("/test/hadoop/file"); + assertFalse("Parent doesn't exist", fs.exists(path.getParent())); + createFile(path); + + assertTrue("Exists", fs.exists(path)); + assertEquals("Length", data.length, fs.getFileStatus(path).getLen()); + assertTrue("Parent exists", fs.exists(path.getParent())); + } + + public void testDeleteNonExistentFile() throws IOException { + Path path = path("/test/hadoop/file"); + assertFalse("Doesn't exist", fs.exists(path)); + assertFalse("No deletion", fs.delete(path, true)); + } + + public void testDeleteRecursively() throws IOException { + Path dir = path("/test/hadoop"); + Path file = path("/test/hadoop/file"); + Path subdir = path("/test/hadoop/subdir"); + + createFile(file); + assertTrue("Created subdir", fs.mkdirs(subdir)); + + assertTrue("File exists", fs.exists(file)); + assertTrue("Dir exists", fs.exists(dir)); + assertTrue("Subdir exists", fs.exists(subdir)); + + try { + fs.delete(dir, false); + fail("Should throw IOException."); + } catch (IOException e) { + // expected + } + assertTrue("File still exists", fs.exists(file)); + assertTrue("Dir still exists", fs.exists(dir)); + assertTrue("Subdir still exists", fs.exists(subdir)); + + assertTrue("Deleted", fs.delete(dir, true)); + assertFalse("File doesn't exist", fs.exists(file)); + assertFalse("Dir doesn't exist", fs.exists(dir)); + assertFalse("Subdir doesn't exist", fs.exists(subdir)); + } + + public void testDeleteEmptyDirectory() throws IOException { + Path dir = path("/test/hadoop"); + assertTrue(fs.mkdirs(dir)); + assertTrue("Dir exists", fs.exists(dir)); + assertTrue("Deleted", fs.delete(dir, false)); + assertFalse("Dir doesn't exist", fs.exists(dir)); + } + + public void testRenameNonExistentPath() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/path"); + Path dst = path("/test/new/newpath"); + rename(src, dst, false, false, false); + } + + public void testRenameFileMoveToNonExistentDirectory() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/file"); + createFile(src); + Path dst = path("/test/new/newfile"); + rename(src, dst, false, true, false); + } + + public void testRenameFileMoveToExistingDirectory() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/file"); + createFile(src); + Path dst = path("/test/new/newfile"); + fs.mkdirs(dst.getParent()); + rename(src, dst, true, false, true); + } + + public void testRenameFileAsExistingFile() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/file"); + createFile(src); + Path dst = path("/test/new/newfile"); + createFile(dst); + rename(src, dst, false, true, true); + } + + public void testRenameFileAsExistingDirectory() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/file"); + createFile(src); + Path dst = path("/test/new/newdir"); + fs.mkdirs(dst); + rename(src, dst, true, false, true); + assertTrue("Destination changed", + fs.exists(path("/test/new/newdir/file"))); + } + + public void testRenameDirectoryMoveToNonExistentDirectory() + throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/dir"); + fs.mkdirs(src); + Path dst = path("/test/new/newdir"); + rename(src, dst, false, true, false); + } + + public void testRenameDirectoryMoveToExistingDirectory() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/dir"); + fs.mkdirs(src); + createFile(path("/test/hadoop/dir/file1")); + createFile(path("/test/hadoop/dir/subdir/file2")); + + Path dst = path("/test/new/newdir"); + fs.mkdirs(dst.getParent()); + rename(src, dst, true, false, true); + + assertFalse("Nested file1 exists", + fs.exists(path("/test/hadoop/dir/file1"))); + assertFalse("Nested file2 exists", + fs.exists(path("/test/hadoop/dir/subdir/file2"))); + assertTrue("Renamed nested file1 exists", + fs.exists(path("/test/new/newdir/file1"))); + assertTrue("Renamed nested exists", + fs.exists(path("/test/new/newdir/subdir/file2"))); + } + + public void testRenameDirectoryAsExistingFile() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/dir"); + fs.mkdirs(src); + Path dst = path("/test/new/newfile"); + createFile(dst); + rename(src, dst, false, true, true); + } + + public void testRenameDirectoryAsExistingDirectory() throws Exception { + if (!renameSupported()) return; + + Path src = path("/test/hadoop/dir"); + fs.mkdirs(src); + createFile(path("/test/hadoop/dir/file1")); + createFile(path("/test/hadoop/dir/subdir/file2")); + + Path dst = path("/test/new/newdir"); + fs.mkdirs(dst); + rename(src, dst, true, false, true); + assertTrue("Destination changed", + fs.exists(path("/test/new/newdir/dir"))); + assertFalse("Nested file1 exists", + fs.exists(path("/test/hadoop/dir/file1"))); + assertFalse("Nested file2 exists", + fs.exists(path("/test/hadoop/dir/subdir/file2"))); + assertTrue("Renamed nested file1 exists", + fs.exists(path("/test/new/newdir/dir/file1"))); + assertTrue("Renamed nested exists", + fs.exists(path("/test/new/newdir/dir/subdir/file2"))); + } + + public void testInputStreamClosedTwice() throws IOException { + //HADOOP-4760 according to Closeable#close() closing already-closed + //streams should have no effect. + Path src = path("/test/hadoop/file"); + createFile(src); + FSDataInputStream in = fs.open(src); + in.close(); + in.close(); + } + + public void testOutputStreamClosedTwice() throws IOException { + //HADOOP-4760 according to Closeable#close() closing already-closed + //streams should have no effect. + Path src = path("/test/hadoop/file"); + FSDataOutputStream out = fs.create(src); + out.writeChar('H'); //write some data + out.close(); + out.close(); + } + + protected Path path(String pathString) { + return new Path(pathString).makeQualified(fs); + } + + protected void createFile(Path path) throws IOException { + FSDataOutputStream out = fs.create(path); + out.write(data, 0, data.length); + out.close(); + } + + private void rename(Path src, Path dst, boolean renameSucceeded, + boolean srcExists, boolean dstExists) throws IOException { + assertEquals("Rename result", renameSucceeded, fs.rename(src, dst)); + assertEquals("Source exists", srcExists, fs.exists(src)); + assertEquals("Destination exists", dstExists, fs.exists(dst)); + } +} diff --git a/src/test/org/apache/hadoop/fs/TestChecksumFileSystem.java b/src/test/org/apache/hadoop/fs/TestChecksumFileSystem.java new file mode 100644 index 00000000000..c55fc3ae414 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestChecksumFileSystem.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs; + +import java.net.URI; + +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.conf.Configuration; +import junit.framework.TestCase; + +public class TestChecksumFileSystem extends TestCase { + public void testgetChecksumLength() throws Exception { + assertEquals(8, ChecksumFileSystem.getChecksumLength(0L, 512)); + assertEquals(12, ChecksumFileSystem.getChecksumLength(1L, 512)); + assertEquals(12, ChecksumFileSystem.getChecksumLength(512L, 512)); + assertEquals(16, ChecksumFileSystem.getChecksumLength(513L, 512)); + assertEquals(16, ChecksumFileSystem.getChecksumLength(1023L, 512)); + assertEquals(16, ChecksumFileSystem.getChecksumLength(1024L, 512)); + assertEquals(408, ChecksumFileSystem.getChecksumLength(100L, 1)); + assertEquals(4000000000008L, + ChecksumFileSystem.getChecksumLength(10000000000000L, 10)); + } + + public void testVerifyChecksum() throws Exception { + String TEST_ROOT_DIR + = System.getProperty("test.build.data","build/test/data/work-dir/localfs"); + + Configuration conf = new Configuration(); + LocalFileSystem localFs = FileSystem.getLocal(conf); + Path testPath = new Path(TEST_ROOT_DIR, "testPath"); + Path testPath11 = new Path(TEST_ROOT_DIR, "testPath11"); + FSDataOutputStream fout = localFs.create(testPath); + fout.write("testing".getBytes()); + fout.close(); + + fout = localFs.create(testPath11); + fout.write("testing you".getBytes()); + fout.close(); + + localFs.delete(localFs.getChecksumFile(testPath), true); + assertTrue("checksum deleted", !localFs.exists(localFs.getChecksumFile(testPath))); + + //copying the wrong checksum file + FileUtil.copy(localFs, localFs.getChecksumFile(testPath11), localFs, + localFs.getChecksumFile(testPath),false,true,conf); + assertTrue("checksum exists", localFs.exists(localFs.getChecksumFile(testPath))); + + boolean errorRead = false; + try { + TestLocalFileSystem.readFile(localFs, testPath); + }catch(ChecksumException ie) { + errorRead = true; + } + assertTrue("error reading", errorRead); + + //now setting verify false, the read should succeed + localFs.setVerifyChecksum(false); + String str = TestLocalFileSystem.readFile(localFs, testPath); + assertTrue("read", "testing".equals(str)); + + } +} diff --git a/src/test/org/apache/hadoop/fs/TestDFVariations.java b/src/test/org/apache/hadoop/fs/TestDFVariations.java new file mode 100644 index 00000000000..3999050069b --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestDFVariations.java @@ -0,0 +1,63 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.hadoop.fs; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.util.EnumSet; + +public class TestDFVariations extends TestCase { + + public static class XXDF extends DF { + private final String osName; + public XXDF(String osName) throws IOException { + super(new File(System.getProperty("test.build.data","/tmp")), 0L); + this.osName = osName; + } + @Override + public DF.OSType getOSType() { + return DF.getOSType(osName); + } + @Override + protected String[] getExecString() { + switch(getOSType()) { + case OS_TYPE_AIX: + return new String[] { "echo", "IGNORE\n", "/dev/sda3", + "453115160", "400077240", "11%", "18", "skip%", "/foo/bar", "\n" }; + default: + return new String[] { "echo", "IGNORE\n", "/dev/sda3", + "453115160", "53037920", "400077240", "11%", "/foo/bar", "\n" }; + } + } + } + + public void testOSParsing() throws Exception { + for (DF.OSType ost : EnumSet.allOf(DF.OSType.class)) { + XXDF df = new XXDF(ost.getId()); + assertEquals(ost.getId() + " total", 453115160 * 1024L, df.getCapacity()); + assertEquals(ost.getId() + " used", 53037920 * 1024L, df.getUsed()); + assertEquals(ost.getId() + " avail", 400077240 * 1024L, df.getAvailable()); + assertEquals(ost.getId() + " pcnt used", 11, df.getPercentUsed()); + assertEquals(ost.getId() + " mount", "/foo/bar", df.getMount()); + } + } + +} + diff --git a/src/test/org/apache/hadoop/fs/TestDU.java b/src/test/org/apache/hadoop/fs/TestDU.java new file mode 100644 index 00000000000..6df487be55f --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestDU.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Random; + +/** This test makes sure that "DU" does not get to run on each call to getUsed */ +public class TestDU extends TestCase { + final static private File DU_DIR = new File( + System.getProperty("test.build.data","/tmp"), "dutmp"); + + public void setUp() throws IOException { + FileUtil.fullyDelete(DU_DIR); + assertTrue(DU_DIR.mkdirs()); + } + + public void tearDown() throws IOException { + FileUtil.fullyDelete(DU_DIR); + } + + private void createFile(File newFile, int size) throws IOException { + // write random data so that filesystems with compression enabled (e.g., ZFS) + // can't compress the file + Random random = new Random(); + byte[] data = new byte[size]; + random.nextBytes(data); + + newFile.createNewFile(); + RandomAccessFile file = new RandomAccessFile(newFile, "rws"); + + file.write(data); + + file.getFD().sync(); + file.close(); + } + + /** + * Verify that du returns expected used space for a file. + * We assume here that if a file system crates a file of size + * that is a multiple of the block size in this file system, + * then the used size for the file will be exactly that size. + * This is true for most file systems. + * + * @throws IOException + * @throws InterruptedException + */ + public void testDU() throws IOException, InterruptedException { + int writtenSize = 32*1024; // writing 32K + File file = new File(DU_DIR, "data"); + createFile(file, writtenSize); + + Thread.sleep(5000); // let the metadata updater catch up + + DU du = new DU(file, 10000); + du.start(); + long duSize = du.getUsed(); + du.shutdown(); + + assertEquals(writtenSize, duSize); + + //test with 0 interval, will not launch thread + du = new DU(file, 0); + du.start(); + duSize = du.getUsed(); + du.shutdown(); + + assertEquals(writtenSize, duSize); + + //test without launching thread + du = new DU(file, 10000); + duSize = du.getUsed(); + + assertEquals(writtenSize, duSize); + } +} diff --git a/src/test/org/apache/hadoop/fs/TestGetFileBlockLocations.java b/src/test/org/apache/hadoop/fs/TestGetFileBlockLocations.java new file mode 100644 index 00000000000..c85cc988627 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestGetFileBlockLocations.java @@ -0,0 +1,139 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.hadoop.fs; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +/** + * Testing the correctness of FileSystem.getFileBlockLocations. + */ +public class TestGetFileBlockLocations extends TestCase { + private static String TEST_ROOT_DIR = + System.getProperty("test.build.data", "/tmp/testGetFileBlockLocations"); + private static final int FileLength = 4 * 1024 * 1024; // 4MB + private Configuration conf; + private Path path; + private FileSystem fs; + private Random random; + + /** + * @see TestCase#setUp() + */ + @Override + protected void setUp() throws IOException { + conf = new Configuration(); + Path rootPath = new Path(TEST_ROOT_DIR); + path = new Path(rootPath, "TestGetFileBlockLocations"); + fs = rootPath.getFileSystem(conf); + FSDataOutputStream fsdos = fs.create(path, true); + byte[] buffer = new byte[1024]; + while (fsdos.getPos() < FileLength) { + fsdos.write(buffer); + } + fsdos.close(); + random = new Random(System.nanoTime()); + } + + private void oneTest(int offBegin, int offEnd, FileStatus status) + throws IOException { + if (offBegin > offEnd) { + int tmp = offBegin; + offBegin = offEnd; + offEnd = tmp; + } + BlockLocation[] locations = + fs.getFileBlockLocations(status, offBegin, offEnd - offBegin); + if (offBegin < status.getLen()) { + Arrays.sort(locations, new Comparator() { + + @Override + public int compare(BlockLocation arg0, BlockLocation arg1) { + long cmprv = arg0.getOffset() - arg1.getOffset(); + if (cmprv < 0) return -1; + if (cmprv > 0) return 1; + cmprv = arg0.getLength() - arg1.getLength(); + if (cmprv < 0) return -1; + if (cmprv > 0) return 1; + return 0; + } + + }); + offBegin = (int) Math.min(offBegin, status.getLen() - 1); + offEnd = (int) Math.min(offEnd, status.getLen()); + BlockLocation first = locations[0]; + BlockLocation last = locations[locations.length - 1]; + assertTrue(first.getOffset() <= offBegin); + assertTrue(offEnd <= last.getOffset() + last.getLength()); + } else { + assertTrue(locations.length == 0); + } + } + /** + * @see TestCase#tearDown() + */ + @Override + protected void tearDown() throws IOException { + fs.delete(path, true); + fs.close(); + } + + public void testFailureNegativeParameters() throws IOException { + FileStatus status = fs.getFileStatus(path); + try { + BlockLocation[] locations = fs.getFileBlockLocations(status, -1, 100); + fail("Expecting exception being throw"); + } catch (IllegalArgumentException e) { + + } + + try { + BlockLocation[] locations = fs.getFileBlockLocations(status, 100, -1); + fail("Expecting exception being throw"); + } catch (IllegalArgumentException e) { + + } + } + + public void testGetFileBlockLocations1() throws IOException { + FileStatus status = fs.getFileStatus(path); + oneTest(0, (int) status.getLen(), status); + oneTest(0, (int) status.getLen() * 2, status); + oneTest((int) status.getLen() * 2, (int) status.getLen() * 4, status); + oneTest((int) status.getLen() / 2, (int) status.getLen() * 3, status); + for (int i = 0; i < 10; ++i) { + oneTest((int) status.getLen() * i / 10, (int) status.getLen() * (i + 1) + / 10, status); + } + } + + public void testGetFileBlockLocations2() throws IOException { + FileStatus status = fs.getFileStatus(path); + for (int i = 0; i < 1000; ++i) { + int offBegin = random.nextInt((int) (2 * status.getLen())); + int offEnd = random.nextInt((int) (2 * status.getLen())); + oneTest(offBegin, offEnd, status); + } + } +} diff --git a/src/test/org/apache/hadoop/fs/TestGlobExpander.java b/src/test/org/apache/hadoop/fs/TestGlobExpander.java new file mode 100644 index 00000000000..b0466b80229 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestGlobExpander.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import java.io.IOException; +import java.util.List; + +import junit.framework.TestCase; + +public class TestGlobExpander extends TestCase { + + public void testExpansionIsIdentical() throws IOException { + checkExpansionIsIdentical(""); + checkExpansionIsIdentical("/}"); + checkExpansionIsIdentical("/}{a,b}"); + checkExpansionIsIdentical("{/"); + checkExpansionIsIdentical("{a}"); + checkExpansionIsIdentical("{a,b}/{b,c}"); + checkExpansionIsIdentical("p\\{a/b,c/d\\}s"); + checkExpansionIsIdentical("p{a\\/b,c\\/d}s"); + } + + public void testExpansion() throws IOException { + checkExpansion("{a/b}", "a/b"); + checkExpansion("/}{a/b}", "/}a/b"); + checkExpansion("p{a/b,c/d}s", "pa/bs", "pc/ds"); + checkExpansion("{a/b,c/d,{e,f}}", "a/b", "c/d", "{e,f}"); + checkExpansion("{a/b,c/d}{e,f}", "a/b{e,f}", "c/d{e,f}"); + checkExpansion("{a,b}/{b,{c/d,e/f}}", "{a,b}/b", "{a,b}/c/d", "{a,b}/e/f"); + checkExpansion("{a,b}/{c/\\d}", "{a,b}/c/d"); + } + + private void checkExpansionIsIdentical(String filePattern) throws IOException { + checkExpansion(filePattern, filePattern); + } + + private void checkExpansion(String filePattern, String... expectedExpansions) + throws IOException { + List actualExpansions = GlobExpander.expand(filePattern); + assertEquals("Different number of expansions", expectedExpansions.length, + actualExpansions.size()); + for (int i = 0; i < expectedExpansions.length; i++) { + assertEquals("Expansion of " + filePattern, expectedExpansions[i], + actualExpansions.get(i)); + } + } +} diff --git a/src/test/org/apache/hadoop/fs/TestLocalDirAllocator.java b/src/test/org/apache/hadoop/fs/TestLocalDirAllocator.java new file mode 100644 index 00000000000..eef90308aa9 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestLocalDirAllocator.java @@ -0,0 +1,211 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import java.io.File; +import java.io.IOException; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.Shell; + +import junit.framework.TestCase; + +/** This test LocalDirAllocator works correctly; + * Every test case uses different buffer dirs to + * enforce the AllocatorPerContext initialization. + * This test does not run on Cygwin because under Cygwin + * a directory can be created in a read-only directory + * which breaks this test. + */ +public class TestLocalDirAllocator extends TestCase { + final static private Configuration conf = new Configuration(); + final static private String BUFFER_DIR_ROOT = "build/test/temp"; + final static private Path BUFFER_PATH_ROOT = new Path(BUFFER_DIR_ROOT); + final static private File BUFFER_ROOT = new File(BUFFER_DIR_ROOT); + final static private String BUFFER_DIR[] = new String[] { + BUFFER_DIR_ROOT+"/tmp0", BUFFER_DIR_ROOT+"/tmp1", BUFFER_DIR_ROOT+"/tmp2", + BUFFER_DIR_ROOT+"/tmp3", BUFFER_DIR_ROOT+"/tmp4", BUFFER_DIR_ROOT+"/tmp5", + BUFFER_DIR_ROOT+"/tmp6"}; + final static private Path BUFFER_PATH[] = new Path[] { + new Path(BUFFER_DIR[0]), new Path(BUFFER_DIR[1]), new Path(BUFFER_DIR[2]), + new Path(BUFFER_DIR[3]), new Path(BUFFER_DIR[4]), new Path(BUFFER_DIR[5]), + new Path(BUFFER_DIR[6])}; + final static private String CONTEXT = "dfs.client.buffer.dir"; + final static private String FILENAME = "block"; + final static private LocalDirAllocator dirAllocator = + new LocalDirAllocator(CONTEXT); + static LocalFileSystem localFs; + final static private boolean isWindows = + System.getProperty("os.name").startsWith("Windows"); + final static int SMALL_FILE_SIZE = 100; + static { + try { + localFs = FileSystem.getLocal(conf); + rmBufferDirs(); + } catch(IOException e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + System.exit(-1); + } + } + + private static void rmBufferDirs() throws IOException { + assertTrue(!localFs.exists(BUFFER_PATH_ROOT) || + localFs.delete(BUFFER_PATH_ROOT, true)); + } + + private void validateTempDirCreation(int i) throws IOException { + File result = createTempFile(SMALL_FILE_SIZE); + assertTrue("Checking for " + BUFFER_DIR[i] + " in " + result + " - FAILED!", + result.getPath().startsWith(new File(BUFFER_DIR[i], FILENAME).getPath())); + } + + private File createTempFile() throws IOException { + File result = dirAllocator.createTmpFileForWrite(FILENAME, -1, conf); + result.delete(); + return result; + } + + private File createTempFile(long size) throws IOException { + File result = dirAllocator.createTmpFileForWrite(FILENAME, size, conf); + result.delete(); + return result; + } + + /** Two buffer dirs. The first dir does not exist & is on a read-only disk; + * The second dir exists & is RW + * @throws Exception + */ + public void test0() throws Exception { + if (isWindows) return; + try { + conf.set(CONTEXT, BUFFER_DIR[0]+","+BUFFER_DIR[1]); + assertTrue(localFs.mkdirs(BUFFER_PATH[1])); + BUFFER_ROOT.setReadOnly(); + validateTempDirCreation(1); + validateTempDirCreation(1); + } finally { + Shell.execCommand(new String[]{"chmod", "u+w", BUFFER_DIR_ROOT}); + rmBufferDirs(); + } + } + + /** Two buffer dirs. The first dir exists & is on a read-only disk; + * The second dir exists & is RW + * @throws Exception + */ + public void test1() throws Exception { + if (isWindows) return; + try { + conf.set(CONTEXT, BUFFER_DIR[1]+","+BUFFER_DIR[2]); + assertTrue(localFs.mkdirs(BUFFER_PATH[2])); + BUFFER_ROOT.setReadOnly(); + validateTempDirCreation(2); + validateTempDirCreation(2); + } finally { + Shell.execCommand(new String[]{"chmod", "u+w", BUFFER_DIR_ROOT}); + rmBufferDirs(); + } + } + /** Two buffer dirs. Both do not exist but on a RW disk. + * Check if tmp dirs are allocated in a round-robin + */ + public void test2() throws Exception { + if (isWindows) return; + try { + conf.set(CONTEXT, BUFFER_DIR[2]+","+BUFFER_DIR[3]); + + // create the first file, and then figure the round-robin sequence + createTempFile(SMALL_FILE_SIZE); + int firstDirIdx = (dirAllocator.getCurrentDirectoryIndex() == 0) ? 2 : 3; + int secondDirIdx = (firstDirIdx == 2) ? 3 : 2; + + // check if tmp dirs are allocated in a round-robin manner + validateTempDirCreation(firstDirIdx); + validateTempDirCreation(secondDirIdx); + validateTempDirCreation(firstDirIdx); + } finally { + rmBufferDirs(); + } + } + + /** Two buffer dirs. Both exists and on a R/W disk. + * Later disk1 becomes read-only. + * @throws Exception + */ + public void test3() throws Exception { + if (isWindows) return; + try { + conf.set(CONTEXT, BUFFER_DIR[3]+","+BUFFER_DIR[4]); + assertTrue(localFs.mkdirs(BUFFER_PATH[3])); + assertTrue(localFs.mkdirs(BUFFER_PATH[4])); + + // create the first file with size, and then figure the round-robin sequence + createTempFile(SMALL_FILE_SIZE); + + int nextDirIdx = (dirAllocator.getCurrentDirectoryIndex() == 0) ? 3 : 4; + validateTempDirCreation(nextDirIdx); + + // change buffer directory 2 to be read only + new File(BUFFER_DIR[4]).setReadOnly(); + validateTempDirCreation(3); + validateTempDirCreation(3); + } finally { + rmBufferDirs(); + } + } + + /** + * Two buffer dirs, on read-write disk. + * + * Try to create a whole bunch of files. + * Verify that they do indeed all get created where they should. + * + * Would ideally check statistical properties of distribution, but + * we don't have the nerve to risk false-positives here. + * + * @throws Exception + */ + static final int TRIALS = 100; + public void test4() throws Exception { + if (isWindows) return; + try { + + conf.set(CONTEXT, BUFFER_DIR[5]+","+BUFFER_DIR[6]); + assertTrue(localFs.mkdirs(BUFFER_PATH[5])); + assertTrue(localFs.mkdirs(BUFFER_PATH[6])); + + int inDir5=0, inDir6=0; + for(int i = 0; i < TRIALS; ++i) { + File result = createTempFile(); + if(result.getPath().startsWith(new File(BUFFER_DIR[5], FILENAME).getPath())) { + inDir5++; + } else if(result.getPath().startsWith(new File(BUFFER_DIR[6], FILENAME).getPath())) { + inDir6++; + } + result.delete(); + } + + assertTrue( inDir5 + inDir6 == TRIALS); + + } finally { + rmBufferDirs(); + } + } + +} diff --git a/src/test/org/apache/hadoop/fs/TestLocalFileSystem.java b/src/test/org/apache/hadoop/fs/TestLocalFileSystem.java new file mode 100644 index 00000000000..b244b9b5df4 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestLocalFileSystem.java @@ -0,0 +1,156 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import org.apache.hadoop.conf.Configuration; +import java.io.*; +import junit.framework.*; + +/** + * This class tests the local file system via the FileSystem abstraction. + */ +public class TestLocalFileSystem extends TestCase { + private static String TEST_ROOT_DIR + = System.getProperty("test.build.data","build/test/data/work-dir/localfs"); + + + static void writeFile(FileSystem fs, Path name) throws IOException { + FSDataOutputStream stm = fs.create(name); + stm.writeBytes("42\n"); + stm.close(); + } + + static String readFile(FileSystem fs, Path name) throws IOException { + byte[] b = new byte[1024]; + int offset = 0; + FSDataInputStream in = fs.open(name); + for(int remaining, n; + (remaining = b.length - offset) > 0 && (n = in.read(b, offset, remaining)) != -1; + offset += n); + in.close(); + + String s = new String(b, 0, offset); + System.out.println("s=" + s); + return s; + } + + private void cleanupFile(FileSystem fs, Path name) throws IOException { + assertTrue(fs.exists(name)); + fs.delete(name, true); + assertTrue(!fs.exists(name)); + } + + /** + * Test the capability of setting the working directory. + */ + public void testWorkingDirectory() throws IOException { + Configuration conf = new Configuration(); + FileSystem fileSys = FileSystem.getLocal(conf); + Path origDir = fileSys.getWorkingDirectory(); + Path subdir = new Path(TEST_ROOT_DIR, "new"); + try { + // make sure it doesn't already exist + assertTrue(!fileSys.exists(subdir)); + // make it and check for it + assertTrue(fileSys.mkdirs(subdir)); + assertTrue(fileSys.isDirectory(subdir)); + + fileSys.setWorkingDirectory(subdir); + + // create a directory and check for it + Path dir1 = new Path("dir1"); + assertTrue(fileSys.mkdirs(dir1)); + assertTrue(fileSys.isDirectory(dir1)); + + // delete the directory and make sure it went away + fileSys.delete(dir1, true); + assertTrue(!fileSys.exists(dir1)); + + // create files and manipulate them. + Path file1 = new Path("file1"); + Path file2 = new Path("sub/file2"); + writeFile(fileSys, file1); + fileSys.copyFromLocalFile(file1, file2); + assertTrue(fileSys.exists(file1)); + assertTrue(fileSys.isFile(file1)); + cleanupFile(fileSys, file2); + fileSys.copyToLocalFile(file1, file2); + cleanupFile(fileSys, file2); + + // try a rename + fileSys.rename(file1, file2); + assertTrue(!fileSys.exists(file1)); + assertTrue(fileSys.exists(file2)); + fileSys.rename(file2, file1); + + // try reading a file + InputStream stm = fileSys.open(file1); + byte[] buffer = new byte[3]; + int bytesRead = stm.read(buffer, 0, 3); + assertEquals("42\n", new String(buffer, 0, bytesRead)); + stm.close(); + } finally { + fileSys.setWorkingDirectory(origDir); + fileSys.delete(subdir, true); + } + } + + public void testCopy() throws IOException { + Configuration conf = new Configuration(); + LocalFileSystem fs = FileSystem.getLocal(conf); + Path src = new Path(TEST_ROOT_DIR, "dingo"); + Path dst = new Path(TEST_ROOT_DIR, "yak"); + writeFile(fs, src); + assertTrue(FileUtil.copy(fs, src, fs, dst, true, false, conf)); + assertTrue(!fs.exists(src) && fs.exists(dst)); + assertTrue(FileUtil.copy(fs, dst, fs, src, false, false, conf)); + assertTrue(fs.exists(src) && fs.exists(dst)); + assertTrue(FileUtil.copy(fs, src, fs, dst, true, true, conf)); + assertTrue(!fs.exists(src) && fs.exists(dst)); + fs.mkdirs(src); + assertTrue(FileUtil.copy(fs, dst, fs, src, false, false, conf)); + Path tmp = new Path(src, dst.getName()); + assertTrue(fs.exists(tmp) && fs.exists(dst)); + assertTrue(FileUtil.copy(fs, dst, fs, src, false, true, conf)); + assertTrue(fs.delete(tmp, true)); + fs.mkdirs(tmp); + try { + FileUtil.copy(fs, dst, fs, src, true, true, conf); + fail("Failed to detect existing dir"); + } catch (IOException e) { } + } + + public void testHomeDirectory() throws IOException { + Configuration conf = new Configuration(); + FileSystem fileSys = FileSystem.getLocal(conf); + Path home = new Path(System.getProperty("user.home")) + .makeQualified(fileSys); + Path fsHome = fileSys.getHomeDirectory(); + assertEquals(home, fsHome); + } + + public void testPathEscapes() throws IOException { + Configuration conf = new Configuration(); + FileSystem fs = FileSystem.getLocal(conf); + Path path = new Path(TEST_ROOT_DIR, "foo%bar"); + writeFile(fs, path); + FileStatus status = fs.getFileStatus(path); + assertEquals(path.makeQualified(fs), status.getPath()); + cleanupFile(fs, path); + } +} diff --git a/src/test/org/apache/hadoop/fs/TestLocalFileSystemPermission.java b/src/test/org/apache/hadoop/fs/TestLocalFileSystemPermission.java new file mode 100644 index 00000000000..f68cdb66cdf --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestLocalFileSystemPermission.java @@ -0,0 +1,157 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.permission.*; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.util.Shell; + +import java.io.*; +import java.util.*; + +import junit.framework.*; + +/** + * This class tests the local file system via the FileSystem abstraction. + */ +public class TestLocalFileSystemPermission extends TestCase { + static final String TEST_PATH_PREFIX = new Path(System.getProperty( + "test.build.data", "/tmp")).toString().replace(' ', '_') + + "/" + TestLocalFileSystemPermission.class.getSimpleName() + "_"; + + { + try { + ((org.apache.commons.logging.impl.Log4JLogger)FileSystem.LOG).getLogger() + .setLevel(org.apache.log4j.Level.DEBUG); + } + catch(Exception e) { + System.out.println("Cannot change log level\n" + + StringUtils.stringifyException(e)); + } + } + + private Path writeFile(FileSystem fs, String name) throws IOException { + Path f = new Path(TEST_PATH_PREFIX + name); + FSDataOutputStream stm = fs.create(f); + stm.writeBytes("42\n"); + stm.close(); + return f; + } + + private void cleanupFile(FileSystem fs, Path name) throws IOException { + assertTrue(fs.exists(name)); + fs.delete(name, true); + assertTrue(!fs.exists(name)); + } + + /** Test LocalFileSystem.setPermission */ + public void testLocalFSsetPermission() throws IOException { + if (Path.WINDOWS) { + System.out.println("Cannot run test for Windows"); + return; + } + Configuration conf = new Configuration(); + LocalFileSystem localfs = FileSystem.getLocal(conf); + String filename = "foo"; + Path f = writeFile(localfs, filename); + try { + System.out.println(filename + ": " + getPermission(localfs, f)); + } + catch(Exception e) { + System.out.println(StringUtils.stringifyException(e)); + System.out.println("Cannot run test"); + return; + } + + try { + // create files and manipulate them. + FsPermission all = new FsPermission((short)0777); + FsPermission none = new FsPermission((short)0); + + localfs.setPermission(f, none); + assertEquals(none, getPermission(localfs, f)); + + localfs.setPermission(f, all); + assertEquals(all, getPermission(localfs, f)); + } + finally {cleanupFile(localfs, f);} + } + + FsPermission getPermission(LocalFileSystem fs, Path p) throws IOException { + return fs.getFileStatus(p).getPermission(); + } + + /** Test LocalFileSystem.setOwner */ + public void testLocalFSsetOwner() throws IOException { + if (Path.WINDOWS) { + System.out.println("Cannot run test for Windows"); + return; + } + + Configuration conf = new Configuration(); + LocalFileSystem localfs = FileSystem.getLocal(conf); + String filename = "bar"; + Path f = writeFile(localfs, filename); + List groups = null; + try { + groups = getGroups(); + System.out.println(filename + ": " + getPermission(localfs, f)); + } + catch(IOException e) { + System.out.println(StringUtils.stringifyException(e)); + System.out.println("Cannot run test"); + return; + } + if (groups == null || groups.size() < 1) { + System.out.println("Cannot run test: need at least one group. groups=" + + groups); + return; + } + + // create files and manipulate them. + try { + String g0 = groups.get(0); + localfs.setOwner(f, null, g0); + assertEquals(g0, getGroup(localfs, f)); + + if (groups.size() > 1) { + String g1 = groups.get(1); + localfs.setOwner(f, null, g1); + assertEquals(g1, getGroup(localfs, f)); + } else { + System.out.println("Not testing changing the group since user " + + "belongs to only one group."); + } + } + finally {cleanupFile(localfs, f);} + } + + static List getGroups() throws IOException { + List a = new ArrayList(); + String s = Shell.execCommand(Shell.getGROUPS_COMMAND()); + for(StringTokenizer t = new StringTokenizer(s); t.hasMoreTokens(); ) { + a.add(t.nextToken()); + } + return a; + } + + String getGroup(LocalFileSystem fs, Path p) throws IOException { + return fs.getFileStatus(p).getGroup(); + } +} diff --git a/src/test/org/apache/hadoop/fs/TestPath.java b/src/test/org/apache/hadoop/fs/TestPath.java new file mode 100644 index 00000000000..4fa28bc77ce --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestPath.java @@ -0,0 +1,152 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs; + +import java.util.*; +import junit.framework.TestCase; + +public class TestPath extends TestCase { + public void testToString() { + toStringTest("/"); + toStringTest("/foo"); + toStringTest("/foo/bar"); + toStringTest("foo"); + toStringTest("foo/bar"); + boolean emptyException = false; + try { + toStringTest(""); + } catch (IllegalArgumentException e) { + // expect to receive an IllegalArgumentException + emptyException = true; + } + assertTrue(emptyException); + if (Path.WINDOWS) { + toStringTest("c:"); + toStringTest("c:/"); + toStringTest("c:foo"); + toStringTest("c:foo/bar"); + toStringTest("c:foo/bar"); + toStringTest("c:/foo/bar"); + } + } + + private void toStringTest(String pathString) { + assertEquals(pathString, new Path(pathString).toString()); + } + + public void testNormalize() { + assertEquals("/", new Path("//").toString()); + assertEquals("/foo", new Path("/foo/").toString()); + assertEquals("/foo", new Path("/foo/").toString()); + assertEquals("foo", new Path("foo/").toString()); + assertEquals("foo", new Path("foo//").toString()); + assertEquals("foo/bar", new Path("foo//bar").toString()); + if (Path.WINDOWS) { + assertEquals("c:/a/b", new Path("c:\\a\\b").toString()); + } + } + + public void testIsAbsolute() { + assertTrue(new Path("/").isAbsolute()); + assertTrue(new Path("/foo").isAbsolute()); + assertFalse(new Path("foo").isAbsolute()); + assertFalse(new Path("foo/bar").isAbsolute()); + assertFalse(new Path(".").isAbsolute()); + if (Path.WINDOWS) { + assertTrue(new Path("c:/a/b").isAbsolute()); + assertFalse(new Path("c:a/b").isAbsolute()); + } + } + + public void testParent() { + assertEquals(new Path("/foo"), new Path("/foo/bar").getParent()); + assertEquals(new Path("foo"), new Path("foo/bar").getParent()); + assertEquals(new Path("/"), new Path("/foo").getParent()); + if (Path.WINDOWS) { + assertEquals(new Path("c:/"), new Path("c:/foo").getParent()); + } + } + + public void testChild() { + assertEquals(new Path("."), new Path(".", ".")); + assertEquals(new Path("/"), new Path("/", ".")); + assertEquals(new Path("/"), new Path(".", "/")); + assertEquals(new Path("/foo"), new Path("/", "foo")); + assertEquals(new Path("/foo/bar"), new Path("/foo", "bar")); + assertEquals(new Path("/foo/bar/baz"), new Path("/foo/bar", "baz")); + assertEquals(new Path("/foo/bar/baz"), new Path("/foo", "bar/baz")); + assertEquals(new Path("foo"), new Path(".", "foo")); + assertEquals(new Path("foo/bar"), new Path("foo", "bar")); + assertEquals(new Path("foo/bar/baz"), new Path("foo", "bar/baz")); + assertEquals(new Path("foo/bar/baz"), new Path("foo/bar", "baz")); + assertEquals(new Path("/foo"), new Path("/bar", "/foo")); + if (Path.WINDOWS) { + assertEquals(new Path("c:/foo"), new Path("/bar", "c:/foo")); + assertEquals(new Path("c:/foo"), new Path("d:/bar", "c:/foo")); + } + } + + public void testEquals() { + assertFalse(new Path("/").equals(new Path("/foo"))); + } + + public void testDots() { + // Test Path(String) + assertEquals(new Path("/foo/bar/baz").toString(), "/foo/bar/baz"); + assertEquals(new Path("/foo/bar", ".").toString(), "/foo/bar"); + assertEquals(new Path("/foo/bar/../baz").toString(), "/foo/baz"); + assertEquals(new Path("/foo/bar/./baz").toString(), "/foo/bar/baz"); + assertEquals(new Path("/foo/bar/baz/../../fud").toString(), "/foo/fud"); + assertEquals(new Path("/foo/bar/baz/.././../fud").toString(), "/foo/fud"); + assertEquals(new Path("../../foo/bar").toString(), "../../foo/bar"); + assertEquals(new Path(".././../foo/bar").toString(), "../../foo/bar"); + assertEquals(new Path("./foo/bar/baz").toString(), "foo/bar/baz"); + assertEquals(new Path("/foo/bar/../../baz/boo").toString(), "/baz/boo"); + assertEquals(new Path("foo/bar/").toString(), "foo/bar"); + assertEquals(new Path("foo/bar/../baz").toString(), "foo/baz"); + assertEquals(new Path("foo/bar/../../baz/boo").toString(), "baz/boo"); + + + // Test Path(Path,Path) + assertEquals(new Path("/foo/bar", "baz/boo").toString(), "/foo/bar/baz/boo"); + assertEquals(new Path("foo/bar/","baz/bud").toString(), "foo/bar/baz/bud"); + + assertEquals(new Path("/foo/bar","../../boo/bud").toString(), "/boo/bud"); + assertEquals(new Path("foo/bar","../../boo/bud").toString(), "boo/bud"); + assertEquals(new Path(".","boo/bud").toString(), "boo/bud"); + + assertEquals(new Path("/foo/bar/baz","../../boo/bud").toString(), "/foo/boo/bud"); + assertEquals(new Path("foo/bar/baz","../../boo/bud").toString(), "foo/boo/bud"); + + + assertEquals(new Path("../../","../../boo/bud").toString(), "../../../../boo/bud"); + assertEquals(new Path("../../foo","../../../boo/bud").toString(), "../../../../boo/bud"); + assertEquals(new Path("../../foo/bar","../boo/bud").toString(), "../../foo/boo/bud"); + + assertEquals(new Path("foo/bar/baz","../../..").toString(), ""); + assertEquals(new Path("foo/bar/baz","../../../../..").toString(), "../.."); + } + + public void testScheme() throws java.io.IOException { + assertEquals("foo:/bar", new Path("foo:/","/bar").toString()); + assertEquals("foo://bar/baz", new Path("foo://bar/","/baz").toString()); + } + + +} diff --git a/src/test/org/apache/hadoop/fs/TestTrash.java b/src/test/org/apache/hadoop/fs/TestTrash.java new file mode 100644 index 00000000000..cff1f2419b7 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestTrash.java @@ -0,0 +1,313 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + + +import junit.framework.TestCase; +import java.io.File; +import java.io.IOException; +import java.io.DataOutputStream; +import java.net.URI; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FsShell; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.Trash; +import org.apache.hadoop.fs.LocalFileSystem; + +/** + * This class tests commands from Trash. + */ +public class TestTrash extends TestCase { + + private final static Path TEST_DIR = + new Path(new File(System.getProperty("test.build.data","/tmp") + ).toURI().toString().replace(' ', '+'), "testTrash"); + + protected static Path writeFile(FileSystem fs, Path f) throws IOException { + DataOutputStream out = fs.create(f); + out.writeBytes("dhruba: " + f); + out.close(); + assertTrue(fs.exists(f)); + return f; + } + + protected static Path mkdir(FileSystem fs, Path p) throws IOException { + assertTrue(fs.mkdirs(p)); + assertTrue(fs.exists(p)); + assertTrue(fs.getFileStatus(p).isDir()); + return p; + } + + // check that the specified file is in Trash + protected static void checkTrash(FileSystem fs, Path trashRoot, + Path path) throws IOException { + Path p = new Path(trashRoot+"/"+ path.toUri().getPath()); + assertTrue(fs.exists(p)); + } + + // check that the specified file is not in Trash + static void checkNotInTrash(FileSystem fs, Path trashRoot, String pathname) + throws IOException { + Path p = new Path(trashRoot+"/"+ new Path(pathname).getName()); + assertTrue(!fs.exists(p)); + } + + protected static void trashShell(final FileSystem fs, final Path base) + throws IOException { + Configuration conf = new Configuration(); + conf.set("fs.trash.interval", "10"); // 10 minute + conf.set("fs.default.name", fs.getUri().toString()); + FsShell shell = new FsShell(); + shell.setConf(conf); + Path trashRoot = null; + + // First create a new directory with mkdirs + Path myPath = new Path(base, "test/mkdirs"); + mkdir(fs, myPath); + + // Second, create a file in that directory. + Path myFile = new Path(base, "test/mkdirs/myFile"); + writeFile(fs, myFile); + + // Verify that expunge without Trash directory + // won't throw Exception + { + String[] args = new String[1]; + args[0] = "-expunge"; + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + } + + // Verify that we succeed in removing the file we created. + // This should go into Trash. + { + String[] args = new String[2]; + args[0] = "-rm"; + args[1] = myFile.toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + + trashRoot = shell.getCurrentTrashDir(); + checkTrash(fs, trashRoot, myFile); + } + + // Verify that we can recreate the file + writeFile(fs, myFile); + + // Verify that we succeed in removing the file we re-created + { + String[] args = new String[2]; + args[0] = "-rm"; + args[1] = new Path(base, "test/mkdirs/myFile").toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + } + + // Verify that we can recreate the file + writeFile(fs, myFile); + + // Verify that we succeed in removing the whole directory + // along with the file inside it. + { + String[] args = new String[2]; + args[0] = "-rmr"; + args[1] = new Path(base, "test/mkdirs").toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + } + + // recreate directory + mkdir(fs, myPath); + + // Verify that we succeed in removing the whole directory + { + String[] args = new String[2]; + args[0] = "-rmr"; + args[1] = new Path(base, "test/mkdirs").toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + } + + // Check that we can delete a file from the trash + { + Path toErase = new Path(trashRoot, "toErase"); + int retVal = -1; + writeFile(fs, toErase); + try { + retVal = shell.run(new String[] {"-rm", toErase.toString()}); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(retVal == 0); + checkNotInTrash (fs, trashRoot, toErase.toString()); + checkNotInTrash (fs, trashRoot, toErase.toString()+".1"); + } + + // simulate Trash removal + { + String[] args = new String[1]; + args[0] = "-expunge"; + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + } + + // verify that after expunging the Trash, it really goes away + checkNotInTrash(fs, trashRoot, new Path(base, "test/mkdirs/myFile").toString()); + + // recreate directory and file + mkdir(fs, myPath); + writeFile(fs, myFile); + + // remove file first, then remove directory + { + String[] args = new String[2]; + args[0] = "-rm"; + args[1] = myFile.toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + checkTrash(fs, trashRoot, myFile); + + args = new String[2]; + args[0] = "-rmr"; + args[1] = myPath.toString(); + val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == 0); + checkTrash(fs, trashRoot, myPath); + } + + // attempt to remove parent of trash + { + String[] args = new String[2]; + args[0] = "-rmr"; + args[1] = trashRoot.getParent().getParent().toString(); + int val = -1; + try { + val = shell.run(args); + } catch (Exception e) { + System.err.println("Exception raised from Trash.run " + + e.getLocalizedMessage()); + } + assertTrue(val == -1); + assertTrue(fs.exists(trashRoot)); + } + } + + public static void trashNonDefaultFS(Configuration conf) throws IOException { + conf.set("fs.trash.interval", "10"); // 10 minute + // attempt non-default FileSystem trash + { + final FileSystem lfs = FileSystem.getLocal(conf); + Path p = TEST_DIR; + Path f = new Path(p, "foo/bar"); + if (lfs.exists(p)) { + lfs.delete(p, true); + } + try { + f = writeFile(lfs, f); + + FileSystem.closeAll(); + FileSystem localFs = FileSystem.get(URI.create("file:///"), conf); + Trash lTrash = new Trash(localFs, conf); + lTrash.moveToTrash(f.getParent()); + checkTrash(localFs, lTrash.getCurrentTrashDir(), f); + } finally { + if (lfs.exists(p)) { + lfs.delete(p, true); + } + } + } + } + + public void testTrash() throws IOException { + Configuration conf = new Configuration(); + conf.setClass("fs.file.impl", TestLFS.class, FileSystem.class); + trashShell(FileSystem.getLocal(conf), TEST_DIR); + } + + public void testNonDefaultFS() throws IOException { + Configuration conf = new Configuration(); + conf.setClass("fs.file.impl", TestLFS.class, FileSystem.class); + conf.set("fs.default.name", "invalid://host/bar/foo"); + trashNonDefaultFS(conf); + } + + static class TestLFS extends LocalFileSystem { + Path home; + TestLFS() { + this(TEST_DIR); + } + TestLFS(Path home) { + super(); + this.home = home; + } + public Path getHomeDirectory() { + return home; + } + } +} diff --git a/src/test/org/apache/hadoop/fs/TestTruncatedInputBug.java b/src/test/org/apache/hadoop/fs/TestTruncatedInputBug.java new file mode 100644 index 00000000000..e7dabf903cd --- /dev/null +++ b/src/test/org/apache/hadoop/fs/TestTruncatedInputBug.java @@ -0,0 +1,109 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs; + +import java.io.DataOutputStream; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +/** + * test for the input truncation bug when mark/reset is used. + * HADOOP-1489 + */ +public class TestTruncatedInputBug extends TestCase { + private static String TEST_ROOT_DIR = + new Path(System.getProperty("test.build.data","/tmp")) + .toString().replace(' ', '+'); + + private void writeFile(FileSystem fileSys, + Path name, int nBytesToWrite) + throws IOException { + DataOutputStream out = fileSys.create(name); + for (int i = 0; i < nBytesToWrite; ++i) { + out.writeByte(0); + } + out.close(); + } + + /** + * When mark() is used on BufferedInputStream, the request + * size on the checksum file system can be small. However, + * checksum file system currently depends on the request size + * >= bytesPerSum to work properly. + */ + public void testTruncatedInputBug() throws IOException { + final int ioBufSize = 512; + final int fileSize = ioBufSize*4; + int filePos = 0; + + Configuration conf = new Configuration(); + conf.setInt("io.file.buffer.size", ioBufSize); + FileSystem fileSys = FileSystem.getLocal(conf); + + try { + // First create a test input file. + Path testFile = new Path(TEST_ROOT_DIR, "HADOOP-1489"); + writeFile(fileSys, testFile, fileSize); + assertTrue(fileSys.exists(testFile)); + assertTrue(fileSys.getFileStatus(testFile).getLen() == fileSize); + + // Now read the file for ioBufSize bytes + FSDataInputStream in = fileSys.open(testFile, ioBufSize); + // seek beyond data buffered by open + filePos += ioBufSize * 2 + (ioBufSize - 10); + in.seek(filePos); + + // read 4 more bytes before marking + for (int i = 0; i < 4; ++i) { + if (in.read() == -1) { + break; + } + ++filePos; + } + + // Now set mark() to trigger the bug + // NOTE: in the fixed code, mark() does nothing (not supported) and + // hence won't trigger this bug. + in.mark(1); + System.out.println("MARKED"); + + // Try to read the rest + while (filePos < fileSize) { + if (in.read() == -1) { + break; + } + ++filePos; + } + in.close(); + + System.out.println("Read " + filePos + " bytes." + + " file size=" + fileSize); + assertTrue(filePos == fileSize); + + } finally { + try { + fileSys.close(); + } catch (Exception e) { + // noop + } + } + } // end testTruncatedInputBug +} diff --git a/src/test/org/apache/hadoop/fs/kfs/KFSEmulationImpl.java b/src/test/org/apache/hadoop/fs/kfs/KFSEmulationImpl.java new file mode 100644 index 00000000000..9c7b5bafef4 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/kfs/KFSEmulationImpl.java @@ -0,0 +1,150 @@ +/** + * + * 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. + * + * @author: Sriram Rao (Kosmix Corp.) + * + * We need to provide the ability to the code in fs/kfs without really + * having a KFS deployment. For this purpose, use the LocalFileSystem + * as a way to "emulate" KFS. + */ + +package org.apache.hadoop.fs.kfs; + +import java.io.IOException; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.BlockLocation; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.util.Progressable; + +public class KFSEmulationImpl implements IFSImpl { + FileSystem localFS; + + public KFSEmulationImpl(Configuration conf) throws IOException { + localFS = FileSystem.getLocal(conf); + } + + public boolean exists(String path) throws IOException { + return localFS.exists(new Path(path)); + } + public boolean isDirectory(String path) throws IOException { + return localFS.isDirectory(new Path(path)); + } + public boolean isFile(String path) throws IOException { + return localFS.isFile(new Path(path)); + } + + public String[] readdir(String path) throws IOException { + FileStatus[] p = localFS.listStatus(new Path(path)); + String[] entries = null; + + if (p == null) { + return null; + } + + entries = new String[p.length]; + for (int i = 0; i < p.length; i++) + entries[i] = p[i].getPath().toString(); + return entries; + } + + public FileStatus[] readdirplus(Path path) throws IOException { + return localFS.listStatus(path); + } + + public int mkdirs(String path) throws IOException { + if (localFS.mkdirs(new Path(path))) + return 0; + + return -1; + } + + public int rename(String source, String dest) throws IOException { + if (localFS.rename(new Path(source), new Path(dest))) + return 0; + return -1; + } + + public int rmdir(String path) throws IOException { + if (isDirectory(path)) { + // the directory better be empty + String[] dirEntries = readdir(path); + if ((dirEntries.length <= 2) && (localFS.delete(new Path(path), true))) + return 0; + } + return -1; + } + + public int remove(String path) throws IOException { + if (isFile(path) && (localFS.delete(new Path(path), true))) + return 0; + return -1; + } + + public long filesize(String path) throws IOException { + return localFS.getFileStatus(new Path(path)).getLen(); + } + public short getReplication(String path) throws IOException { + return 1; + } + public short setReplication(String path, short replication) throws IOException { + return 1; + } + public String[][] getDataLocation(String path, long start, long len) throws IOException { + BlockLocation[] blkLocations = + localFS.getFileBlockLocations(localFS.getFileStatus(new Path(path)), + start, len); + if ((blkLocations == null) || (blkLocations.length == 0)) { + return new String[0][]; + } + int blkCount = blkLocations.length; + String[][]hints = new String[blkCount][]; + for (int i=0; i < blkCount ; i++) { + String[] hosts = blkLocations[i].getHosts(); + hints[i] = new String[hosts.length]; + hints[i] = hosts; + } + return hints; + } + + public long getModificationTime(String path) throws IOException { + FileStatus s = localFS.getFileStatus(new Path(path)); + if (s == null) + return 0; + + return s.getModificationTime(); + } + + public FSDataOutputStream append(String path, int bufferSize, Progressable progress) throws IOException { + // besides path/overwrite, the other args don't matter for + // testing purposes. + return localFS.append(new Path(path)); + } + + public FSDataOutputStream create(String path, short replication, int bufferSize, Progressable progress) throws IOException { + // besides path/overwrite, the other args don't matter for + // testing purposes. + return localFS.create(new Path(path)); + } + + public FSDataInputStream open(String path, int bufferSize) throws IOException { + return localFS.open(new Path(path)); + } + + +}; diff --git a/src/test/org/apache/hadoop/fs/kfs/TestKosmosFileSystem.java b/src/test/org/apache/hadoop/fs/kfs/TestKosmosFileSystem.java new file mode 100644 index 00000000000..c853f2af3f3 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/kfs/TestKosmosFileSystem.java @@ -0,0 +1,204 @@ +/** + * + * 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. + * + * @author: Sriram Rao (Kosmix Corp.) + * + * Unit tests for testing the KosmosFileSystem API implementation. + */ + +package org.apache.hadoop.fs.kfs; + +import java.io.*; +import java.net.*; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.Path; + +import org.apache.hadoop.fs.kfs.KosmosFileSystem; + +public class TestKosmosFileSystem extends TestCase { + + KosmosFileSystem kosmosFileSystem; + KFSEmulationImpl kfsEmul; + Path baseDir; + + @Override + protected void setUp() throws IOException { + Configuration conf = new Configuration(); + + kfsEmul = new KFSEmulationImpl(conf); + kosmosFileSystem = new KosmosFileSystem(kfsEmul); + // a dummy URI; we are not connecting to any setup here + kosmosFileSystem.initialize(URI.create("kfs:///"), conf); + baseDir = new Path(System.getProperty("test.build.data", "/tmp" ) + + "/kfs-test"); + } + + @Override + protected void tearDown() throws Exception { + + } + + // @Test + // Check all the directory API's in KFS + public void testDirs() throws Exception { + Path subDir1 = new Path("dir.1"); + + // make the dir + kosmosFileSystem.mkdirs(baseDir); + assertTrue(kosmosFileSystem.isDirectory(baseDir)); + kosmosFileSystem.setWorkingDirectory(baseDir); + + kosmosFileSystem.mkdirs(subDir1); + assertTrue(kosmosFileSystem.isDirectory(subDir1)); + + assertFalse(kosmosFileSystem.exists(new Path("test1"))); + assertFalse(kosmosFileSystem.isDirectory(new Path("test/dir.2"))); + + FileStatus[] p = kosmosFileSystem.listStatus(baseDir); + assertEquals(p.length, 1); + + kosmosFileSystem.delete(baseDir, true); + assertFalse(kosmosFileSystem.exists(baseDir)); + } + + // @Test + // Check the file API's + public void testFiles() throws Exception { + Path subDir1 = new Path("dir.1"); + Path file1 = new Path("dir.1/foo.1"); + Path file2 = new Path("dir.1/foo.2"); + + kosmosFileSystem.mkdirs(baseDir); + assertTrue(kosmosFileSystem.isDirectory(baseDir)); + kosmosFileSystem.setWorkingDirectory(baseDir); + + kosmosFileSystem.mkdirs(subDir1); + + FSDataOutputStream s1 = kosmosFileSystem.create(file1, true, 4096, (short) 1, (long) 4096, null); + FSDataOutputStream s2 = kosmosFileSystem.create(file2, true, 4096, (short) 1, (long) 4096, null); + + s1.close(); + s2.close(); + + FileStatus[] p = kosmosFileSystem.listStatus(subDir1); + assertEquals(p.length, 2); + + kosmosFileSystem.delete(file1, true); + p = kosmosFileSystem.listStatus(subDir1); + assertEquals(p.length, 1); + + kosmosFileSystem.delete(file2, true); + p = kosmosFileSystem.listStatus(subDir1); + assertEquals(p.length, 0); + + kosmosFileSystem.delete(baseDir, true); + assertFalse(kosmosFileSystem.exists(baseDir)); + } + + // @Test + // Check file/read write + public void testFileIO() throws Exception { + Path subDir1 = new Path("dir.1"); + Path file1 = new Path("dir.1/foo.1"); + + kosmosFileSystem.mkdirs(baseDir); + assertTrue(kosmosFileSystem.isDirectory(baseDir)); + kosmosFileSystem.setWorkingDirectory(baseDir); + + kosmosFileSystem.mkdirs(subDir1); + + FSDataOutputStream s1 = kosmosFileSystem.create(file1, true, 4096, (short) 1, (long) 4096, null); + + int bufsz = 4096; + byte[] data = new byte[bufsz]; + + for (int i = 0; i < data.length; i++) + data[i] = (byte) (i % 16); + + // write 4 bytes and read them back; read API should return a byte per call + s1.write(32); + s1.write(32); + s1.write(32); + s1.write(32); + // write some data + s1.write(data, 0, data.length); + // flush out the changes + s1.close(); + + // Read the stuff back and verify it is correct + FSDataInputStream s2 = kosmosFileSystem.open(file1, 4096); + int v; + long nread = 0; + + v = s2.read(); + assertEquals(v, 32); + v = s2.read(); + assertEquals(v, 32); + v = s2.read(); + assertEquals(v, 32); + v = s2.read(); + assertEquals(v, 32); + + assertEquals(s2.available(), data.length); + + byte[] buf = new byte[bufsz]; + s2.read(buf, 0, buf.length); + nread = s2.getPos(); + + for (int i = 0; i < data.length; i++) + assertEquals(data[i], buf[i]); + + assertEquals(s2.available(), 0); + + s2.close(); + + // append some data to the file + try { + s1 = kosmosFileSystem.append(file1); + for (int i = 0; i < data.length; i++) + data[i] = (byte) (i % 17); + // write the data + s1.write(data, 0, data.length); + // flush out the changes + s1.close(); + + // read it back and validate + s2 = kosmosFileSystem.open(file1, 4096); + s2.seek(nread); + s2.read(buf, 0, buf.length); + for (int i = 0; i < data.length; i++) + assertEquals(data[i], buf[i]); + + s2.close(); + } catch (Exception e) { + System.out.println("append isn't supported by the underlying fs"); + } + + kosmosFileSystem.delete(file1, true); + assertFalse(kosmosFileSystem.exists(file1)); + kosmosFileSystem.delete(subDir1, true); + assertFalse(kosmosFileSystem.exists(subDir1)); + kosmosFileSystem.delete(baseDir, true); + assertFalse(kosmosFileSystem.exists(baseDir)); + } + +} diff --git a/src/test/org/apache/hadoop/fs/loadGenerator/DataGenerator.java b/src/test/org/apache/hadoop/fs/loadGenerator/DataGenerator.java new file mode 100644 index 00000000000..4825bbada50 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/loadGenerator/DataGenerator.java @@ -0,0 +1,160 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.loadGenerator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.util.Tool; +import org.apache.hadoop.util.ToolRunner; + +/** + * This program reads the directory structure and file structure from + * the input directory and creates the namespace in the file system + * specified by the configuration in the specified root. + * All the files are filled with 'a'. + * + * The synopsis of the command is + * java DataGenerator + * -inDir : input directory name where directory/file structures + * are stored. Its default value is the current directory. + * -root : the name of the root directory which the new namespace + * is going to be placed under. + * Its default value is "/testLoadSpace". + */ +public class DataGenerator extends Configured implements Tool { + private File inDir = StructureGenerator.DEFAULT_STRUCTURE_DIRECTORY; + private Path root = DEFAULT_ROOT; + private FileSystem fs; + final static private long BLOCK_SIZE = 10; + final static private String USAGE = "java DataGenerator " + + "-inDir " + + "-root "; + + /** default name of the root where the test namespace will be placed under */ + final static Path DEFAULT_ROOT = new Path("/testLoadSpace"); + + /** Main function. + * It first parses the command line arguments. + * It then reads the directory structure from the input directory + * structure file and creates directory structure in the file system + * namespace. Afterwards it reads the file attributes and creates files + * in the file. All file content is filled with 'a'. + */ + public int run(String[] args) throws Exception { + int exitCode = 0; + exitCode = init(args); + if (exitCode != 0) { + return exitCode; + } + genDirStructure(); + genFiles(); + return exitCode; + } + + /** Parse the command line arguments and initialize the data */ + private int init(String[] args) { + try { // initialize file system handle + fs = FileSystem.get(getConf()); + } catch (IOException ioe) { + System.err.println("Can not initialize the file system: " + + ioe.getLocalizedMessage()); + return -1; + } + + for (int i = 0; i < args.length; i++) { // parse command line + if (args[i].equals("-root")) { + root = new Path(args[++i]); + } else if (args[i].equals("-inDir")) { + inDir = new File(args[++i]); + } else { + System.err.println(USAGE); + ToolRunner.printGenericCommandUsage(System.err); + System.exit(-1); + } + } + return 0; + } + + /** Read directory structure file under the input directory. + * Create each directory under the specified root. + * The directory names are relative to the specified root. + */ + private void genDirStructure() throws IOException { + BufferedReader in = new BufferedReader( + new FileReader(new File(inDir, + StructureGenerator.DIR_STRUCTURE_FILE_NAME))); + String line; + while ((line=in.readLine()) != null) { + fs.mkdirs(new Path(root+line)); + } + } + + /** Read file structure file under the input directory. + * Create each file under the specified root. + * The file names are relative to the root. + */ + private void genFiles() throws IOException { + BufferedReader in = new BufferedReader( + new FileReader(new File(inDir, + StructureGenerator.FILE_STRUCTURE_FILE_NAME))); + String line; + while ((line=in.readLine()) != null) { + String[] tokens = line.split(" "); + if (tokens.length != 2) { + throw new IOException("Expect at most 2 tokens per line: " + line); + } + String fileName = root+tokens[0]; + long fileSize = (long)(BLOCK_SIZE*Double.parseDouble(tokens[1])); + genFile(new Path(fileName), fileSize); + } + } + + /** Create a file with the name file and + * a length of fileSize. The file is filled with character 'a'. + */ + private void genFile(Path file, long fileSize) throws IOException { + FSDataOutputStream out = fs.create(file, true, + getConf().getInt("io.file.buffer.size", 4096), + (short)getConf().getInt("dfs.replication", 3), + fs.getDefaultBlockSize()); + for(long i=0; i: read probability [0, 1] + * with a default value of 0.3333. + * -writeProbability : write probability [0, 1] + * with a default value of 0.3333. + * -root : test space with a default value of /testLoadSpace + * -maxDelayBetweenOps : + * Max delay in the unit of milliseconds between two operations with a + * default value of 0 indicating no delay. + * -numOfThreads : + * number of threads to spawn with a default value of 200. + * -elapsedTime : + * the elapsed time of program with a default value of 0 + * indicating running forever + * -startTime : when the threads start to run. + * -scriptFile : text file to parse for scripted operation + */ +public class LoadGenerator extends Configured implements Tool { + public static final Log LOG = LogFactory.getLog(LoadGenerator.class); + + private volatile boolean shouldRun = true; + private Path root = DataGenerator.DEFAULT_ROOT; + private FileSystem fs; + private int maxDelayBetweenOps = 0; + private int numOfThreads = 200; + private long [] durations = {0}; + private double [] readProbs = {0.3333}; + private double [] writeProbs = {0.3333}; + private volatile int currentIndex = 0; + long totalTime = 0; + private long startTime = System.currentTimeMillis()+10000; + final static private int BLOCK_SIZE = 10; + private ArrayList files = new ArrayList(); // a table of file names + private ArrayList dirs = new ArrayList(); // a table of directory names + private Random r = null; + final private static String USAGE = "java LoadGenerator\n" + + "-readProbability \n" + + "-writeProbability \n" + + "-root \n" + + "-maxDelayBetweenOps \n" + + "-numOfThreads \n" + + "-elapsedTime \n" + + "-startTime \n" + + "-scriptFile "; + final private String hostname; + + /** Constructor */ + public LoadGenerator() throws IOException, UnknownHostException { + InetAddress addr = InetAddress.getLocalHost(); + hostname = addr.getHostName(); + } + + private final static int OPEN = 0; + private final static int LIST = 1; + private final static int CREATE = 2; + private final static int WRITE_CLOSE = 3; + private final static int DELETE = 4; + private final static int TOTAL_OP_TYPES =5; + private long [] executionTime = new long[TOTAL_OP_TYPES]; + private long [] totalNumOfOps = new long[TOTAL_OP_TYPES]; + + /** A thread sends a stream of requests to the NameNode. + * At each iteration, it first decides if it is going to read a file, + * create a file, or listing a directory following the read + * and write probabilities. + * When reading, it randomly picks a file in the test space and reads + * the entire file. When writing, it randomly picks a directory in the + * test space and creates a file whose name consists of the current + * machine's host name and the thread id. The length of the file + * follows Gaussian distribution with an average size of 2 blocks and + * the standard deviation of 1 block. The new file is filled with 'a'. + * Immediately after the file creation completes, the file is deleted + * from the test space. + * While listing, it randomly picks a directory in the test space and + * list the directory content. + * Between two consecutive operations, the thread pauses for a random + * amount of time in the range of [0, maxDelayBetweenOps] + * if the specified max delay is not zero. + * A thread runs for the specified elapsed time if the time isn't zero. + * Otherwise, it runs forever. + */ + private class DFSClientThread extends Thread { + private int id; + private long [] executionTime = new long[TOTAL_OP_TYPES]; + private long [] totalNumOfOps = new long[TOTAL_OP_TYPES]; + private byte[] buffer = new byte[1024]; + + private DFSClientThread(int id) { + this.id = id; + } + + /** Main loop + * Each iteration decides what's the next operation and then pauses. + */ + public void run() { + try { + while (shouldRun) { + nextOp(); + delay(); + } + } catch (Exception ioe) { + System.err.println(ioe.getLocalizedMessage()); + ioe.printStackTrace(); + } + } + + /** Let the thread pause for a random amount of time in the range of + * [0, maxDelayBetweenOps] if the delay is not zero. Otherwise, no pause. + */ + private void delay() throws InterruptedException { + if (maxDelayBetweenOps>0) { + int delay = r.nextInt(maxDelayBetweenOps); + Thread.sleep(delay); + } + } + + /** Perform the next operation. + * + * Depending on the read and write probabilities, the next + * operation could be either read, write, or list. + */ + private void nextOp() throws IOException { + double rn = r.nextDouble(); + int i = currentIndex; + + if(LOG.isDebugEnabled()) + LOG.debug("Thread " + this.id + " moving to index " + i); + + if (rn < readProbs[i]) { + read(); + } else if (rn < readProbs[i] + writeProbs[i]) { + write(); + } else { + list(); + } + } + + /** Read operation randomly picks a file in the test space and reads + * the entire file */ + private void read() throws IOException { + String fileName = files.get(r.nextInt(files.size())); + long startTime = System.currentTimeMillis(); + InputStream in = fs.open(new Path(fileName)); + executionTime[OPEN] += (System.currentTimeMillis()-startTime); + totalNumOfOps[OPEN]++; + while (in.read(buffer) != -1) {} + in.close(); + } + + /** The write operation randomly picks a directory in the + * test space and creates a file whose name consists of the current + * machine's host name and the thread id. The length of the file + * follows Gaussian distribution with an average size of 2 blocks and + * the standard deviation of 1 block. The new file is filled with 'a'. + * Immediately after the file creation completes, the file is deleted + * from the test space. + */ + private void write() throws IOException { + String dirName = dirs.get(r.nextInt(dirs.size())); + Path file = new Path(dirName, hostname+id); + double fileSize = 0; + while ((fileSize = r.nextGaussian()+2)<=0) {} + genFile(file, (long)(fileSize*BLOCK_SIZE)); + long startTime = System.currentTimeMillis(); + fs.delete(file, true); + executionTime[DELETE] += (System.currentTimeMillis()-startTime); + totalNumOfOps[DELETE]++; + } + + /** The list operation randomly picks a directory in the test space and + * list the directory content. + */ + private void list() throws IOException { + String dirName = dirs.get(r.nextInt(dirs.size())); + long startTime = System.currentTimeMillis(); + fs.listStatus(new Path(dirName)); + executionTime[LIST] += (System.currentTimeMillis()-startTime); + totalNumOfOps[LIST]++; + } + } + + /** Main function: + * It first initializes data by parsing the command line arguments. + * It then starts the number of DFSClient threads as specified by + * the user. + * It stops all the threads when the specified elapsed time is passed. + * Before exiting, it prints the average execution for + * each operation and operation throughput. + */ + public int run(String[] args) throws Exception { + int exitCode = init(args); + if (exitCode != 0) { + return exitCode; + } + + barrier(); + + DFSClientThread[] threads = new DFSClientThread[numOfThreads]; + for (int i=0; i 0) { + while(shouldRun) { + Thread.sleep(durations[currentIndex] * 1000); + totalTime += durations[currentIndex]; + + // Are we on the final line of the script? + if( (currentIndex + 1) == durations.length) { + shouldRun = false; + } else { + if(LOG.isDebugEnabled()) { + LOG.debug("Moving to index " + currentIndex + ": r = " + + readProbs[currentIndex] + ", w = " + writeProbs + + " for duration " + durations[currentIndex]); + } + currentIndex++; + } + } + } + + LOG.debug("Done with testing. Waiting for threads to finish."); + for (DFSClientThread thread : threads) { + thread.join(); + for (int i=0; i 1) { + System.err.println( + "The read probability must be [0, 1]: " + readProbs[0]); + return -1; + } + } else if (args[i].equals("-writeProbability")) { + if(scriptSpecified) { + System.err.println("Can't specify probabilities and use script."); + return -1; + } + writeProbs[0] = Double.parseDouble(args[++i]); + if (writeProbs[0] < 0 || writeProbs[0] > 1) { + System.err.println( + "The write probability must be [0, 1]: " + writeProbs[0]); + return -1; + } + } else if (args[i].equals("-root")) { + root = new Path(args[++i]); + } else if (args[i].equals("-maxDelayBetweenOps")) { + maxDelayBetweenOps = Integer.parseInt(args[++i]); // in milliseconds + } else if (args[i].equals("-numOfThreads")) { + numOfThreads = Integer.parseInt(args[++i]); + if (numOfThreads <= 0) { + System.err.println( + "Number of threads must be positive: " + numOfThreads); + return -1; + } + } else if (args[i].equals("-startTime")) { + startTime = Long.parseLong(args[++i]); + } else if (args[i].equals("-elapsedTime")) { + if(scriptSpecified) { + System.err.println("Can't specify elapsedTime and use script."); + return -1; + } + durations[0] = Long.parseLong(args[++i]); + } else if (args[i].equals("-seed")) { + r = new Random(Long.parseLong(args[++i])+hostHashCode); + } else { + System.err.println(USAGE); + ToolRunner.printGenericCommandUsage(System.err); + return -1; + } + } + } catch (NumberFormatException e) { + System.err.println("Illegal parameter: " + e.getLocalizedMessage()); + System.err.println(USAGE); + return -1; + } + + for(int i = 0; i < readProbs.length; i++) { + if (readProbs[i] + writeProbs[i] <0 || readProbs[i]+ writeProbs[i] > 1) { + System.err.println( + "The sum of read probability and write probability must be [0, 1]: " + + readProbs[i] + " " + writeProbs[i]); + return -1; + } + } + + if (r==null) { + r = new Random(System.currentTimeMillis()+hostHashCode); + } + + return initFileDirTables(); + } + + /** + * Read a script file of the form: lines of text with duration in seconds, + * read probability and write probability, separated by white space. + * + * @param filename Script file + * @return 0 if successful, -1 if not + * @throws IOException if errors with file IO + */ + private int loadScriptFile(String filename) throws IOException { + FileReader fr = new FileReader(new File(filename)); + BufferedReader br = new BufferedReader(fr); + ArrayList duration = new ArrayList(); + ArrayList readProb = new ArrayList(); + ArrayList writeProb = new ArrayList(); + int lineNum = 0; + + String line; + // Read script, parse values, build array of duration, read and write probs + while((line = br.readLine()) != null) { + lineNum++; + if(line.startsWith("#") || line.isEmpty()) // skip comments and blanks + continue; + + String[] a = line.split("\\s"); + if(a.length != 3) { + System.err.println("Line " + lineNum + + ": Incorrect number of parameters: " + line); + } + + try { + long d = Long.parseLong(a[0]); + if(d < 0) { + System.err.println("Line " + lineNum + ": Invalid duration: " + d); + return -1; + } + + double r = Double.parseDouble(a[1]); + if(r < 0.0 || r > 1.0 ) { + System.err.println("Line " + lineNum + + ": The read probability must be [0, 1]: " + r); + return -1; + } + + double w = Double.parseDouble(a[2]); + if(w < 0.0 || w > 1.0) { + System.err.println("Line " + lineNum + + ": The read probability must be [0, 1]: " + r); + return -1; + } + + readProb.add(r); + duration.add(d); + writeProb.add(w); + } catch( NumberFormatException nfe) { + System.err.println(lineNum + ": Can't parse: " + line); + return -1; + } + } + + br.close(); + fr.close(); + + // Copy vectors to arrays of values, to avoid autoboxing overhead later + durations = new long[duration.size()]; + readProbs = new double[readProb.size()]; + writeProbs = new double[writeProb.size()]; + + for(int i = 0; i < durations.length; i++) { + durations[i] = duration.get(i); + readProbs[i] = readProb.get(i); + writeProbs[i] = writeProb.get(i); + } + + if(durations[0] == 0) + System.err.println("Initial duration set to 0. " + + "Will loop until stopped manually."); + + return 0; + } + + /** Create a table that contains all directories under root and + * another table that contains all files under root. + */ + private int initFileDirTables() { + try { + initFileDirTables(root); + } catch (IOException e) { + System.err.println(e.getLocalizedMessage()); + e.printStackTrace(); + return -1; + } + if (dirs.isEmpty()) { + System.err.println("The test space " + root + " is empty"); + return -1; + } + if (files.isEmpty()) { + System.err.println("The test space " + root + + " does not have any file"); + return -1; + } + return 0; + } + + /** Create a table that contains all directories under the specified path and + * another table that contains all files under the specified path and + * whose name starts with "_file_". + */ + private void initFileDirTables(Path path) throws IOException { + FileStatus[] stats = fs.listStatus(path); + if (stats != null) { + for (FileStatus stat : stats) { + if (stat.isDir()) { + dirs.add(stat.getPath().toString()); + initFileDirTables(stat.getPath()); + } else { + Path filePath = stat.getPath(); + if (filePath.getName().startsWith(StructureGenerator.FILE_NAME_PREFIX)) { + files.add(filePath.toString()); + } + } + } + } + } + + /** Returns when the current number of seconds from the epoch equals + * the command line argument given by -startTime. + * This allows multiple instances of this program, running on clock + * synchronized nodes, to start at roughly the same time. + */ + private void barrier() { + long sleepTime; + while ((sleepTime = startTime - System.currentTimeMillis()) > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException ex) { + } + } + } + + /** Create a file with a length of fileSize. + * The file is filled with 'a'. + */ + private void genFile(Path file, long fileSize) throws IOException { + long startTime = System.currentTimeMillis(); + FSDataOutputStream out = fs.create(file, true, + getConf().getInt("io.file.buffer.size", 4096), + (short)getConf().getInt("dfs.replication", 3), + fs.getDefaultBlockSize()); + executionTime[CREATE] += (System.currentTimeMillis()-startTime); + totalNumOfOps[CREATE]++; + + for (long i=0; i : maximum depth of the directory tree; default is 5. + -minWidth : minimum number of subdirectories per directories; default is 1 + -maxWidth : maximum number of subdirectories per directories; default is 5 + -numOfFiles <#OfFiles> : the total number of files; default is 10. + -avgFileSize : average size of blocks; default is 1. + -outDir : output directory; default is the current directory. + -seed : random number generator seed; default is the current time. + */ +public class StructureGenerator { + private int maxDepth = 5; + private int minWidth = 1; + private int maxWidth = 5; + private int numOfFiles = 10; + private double avgFileSize = 1; + private File outDir = DEFAULT_STRUCTURE_DIRECTORY; + final static private String USAGE = "java StructureGenerator\n" + + "-maxDepth \n" + + "-minWidth \n" + + "-maxWidth \n" + + "-numOfFiles <#OfFiles>\n" + + "-avgFileSize \n" + + "-outDir \n" + + "-seed "; + + private Random r = null; + + /** Default directory for storing file/directory structure */ + final static File DEFAULT_STRUCTURE_DIRECTORY = new File("."); + /** The name of the file for storing directory structure */ + final static String DIR_STRUCTURE_FILE_NAME = "dirStructure"; + /** The name of the file for storing file structure */ + final static String FILE_STRUCTURE_FILE_NAME = "fileStructure"; + /** The name prefix for the files created by this program */ + final static String FILE_NAME_PREFIX = "_file_"; + + /** + * The main function first parses the command line arguments, + * then generates in-memory directory structure and outputs to a file, + * last generates in-memory files and outputs them to a file. + */ + public int run(String[] args) throws Exception { + int exitCode = 0; + exitCode = init(args); + if (exitCode != 0) { + return exitCode; + } + genDirStructure(); + output(new File(outDir, DIR_STRUCTURE_FILE_NAME)); + genFileStructure(); + outputFiles(new File(outDir, FILE_STRUCTURE_FILE_NAME)); + return exitCode; + } + + /** Parse the command line arguments and initialize the data */ + private int init(String[] args) { + try { + for (int i = 0; i < args.length; i++) { // parse command line + if (args[i].equals("-maxDepth")) { + maxDepth = Integer.parseInt(args[++i]); + if (maxDepth<1) { + System.err.println("maxDepth must be positive: " + maxDepth); + return -1; + } + } else if (args[i].equals("-minWidth")) { + minWidth = Integer.parseInt(args[++i]); + if (minWidth<0) { + System.err.println("minWidth must be positive: " + minWidth); + return -1; + } + } else if (args[i].equals("-maxWidth")) { + maxWidth = Integer.parseInt(args[++i]); + } else if (args[i].equals("-numOfFiles")) { + numOfFiles = Integer.parseInt(args[++i]); + if (numOfFiles<1) { + System.err.println("NumOfFiles must be positive: " + numOfFiles); + return -1; + } + } else if (args[i].equals("-avgFileSize")) { + avgFileSize = Double.parseDouble(args[++i]); + if (avgFileSize<=0) { + System.err.println("AvgFileSize must be positive: " + avgFileSize); + return -1; + } + } else if (args[i].equals("-outDir")) { + outDir = new File(args[++i]); + } else if (args[i].equals("-seed")) { + r = new Random(Long.parseLong(args[++i])); + } else { + System.err.println(USAGE); + ToolRunner.printGenericCommandUsage(System.err); + return -1; + } + } + } catch (NumberFormatException e) { + System.err.println("Illegal parameter: " + e.getLocalizedMessage()); + System.err.println(USAGE); + return -1; + } + + if (maxWidth < minWidth) { + System.err.println( + "maxWidth must be bigger than minWidth: " + maxWidth); + return -1; + } + + if (r==null) { + r = new Random(); + } + return 0; + } + + /** In memory representation of a directory */ + private static class INode { + private String name; + private List children = new ArrayList(); + + /** Constructor */ + private INode(String name) { + this.name = name; + } + + /** Add a child (subdir/file) */ + private void addChild(INode child) { + children.add(child); + } + + /** Output the subtree rooted at the current node. + * Only the leaves are printed. + */ + private void output(PrintStream out, String prefix) { + prefix = prefix==null?name:prefix+"/"+name; + if (children.isEmpty()) { + out.println(prefix); + } else { + for (INode child : children) { + child.output(out, prefix); + } + } + } + + /** Output the files in the subtree rooted at this node */ + protected void outputFiles(PrintStream out, String prefix) { + prefix = prefix==null?name:prefix+"/"+name; + for (INode child : children) { + child.outputFiles(out, prefix); + } + } + + /** Add all the leaves in the subtree to the input list */ + private void getLeaves(List leaves) { + if (children.isEmpty()) { + leaves.add(this); + } else { + for (INode child : children) { + child.getLeaves(leaves); + } + } + } + } + + /** In memory representation of a file */ + private static class FileINode extends INode { + private double numOfBlocks; + + /** constructor */ + private FileINode(String name, double numOfBlocks) { + super(name); + this.numOfBlocks = numOfBlocks; + } + + /** Output a file attribute */ + protected void outputFiles(PrintStream out, String prefix) { + prefix = (prefix == null)?super.name: prefix + "/"+super.name; + out.println(prefix + " " + numOfBlocks); + } + } + + private INode root; + + /** Generates a directory tree with a max depth of maxDepth */ + private void genDirStructure() { + root = genDirStructure("", maxDepth); + } + + /** Generate a directory tree rooted at rootName + * The number of subtree is in the range of [minWidth, maxWidth]. + * The maximum depth of each subtree is in the range of + * [2*maxDepth/3, maxDepth]. + */ + private INode genDirStructure(String rootName, int maxDepth) { + INode root = new INode(rootName); + + if (maxDepth>0) { + maxDepth--; + int minDepth = maxDepth*2/3; + // Figure out the number of subdirectories to generate + int numOfSubDirs = minWidth + r.nextInt(maxWidth-minWidth+1); + // Expand the tree + for (int i=0; i getLeaves() { + List leaveDirs = new ArrayList(); + root.getLeaves(leaveDirs); + return leaveDirs; + } + + /** Decides where to place all the files and its length. + * It first collects all empty directories in the tree. + * For each file, it randomly chooses an empty directory to place the file. + * The file's length is generated using Gaussian distribution. + */ + private void genFileStructure() { + List leaves = getLeaves(); + int totalLeaves = leaves.size(); + for (int i=0; i inodes = new TreeMap(); + private Map blocks = new HashMap(); + + public void initialize(URI uri, Configuration conf) { + this.conf = conf; + } + + public String getVersion() throws IOException { + return "0"; + } + + public void deleteINode(Path path) throws IOException { + inodes.remove(normalize(path)); + } + + public void deleteBlock(Block block) throws IOException { + blocks.remove(block.getId()); + } + + public boolean inodeExists(Path path) throws IOException { + return inodes.containsKey(normalize(path)); + } + + public boolean blockExists(long blockId) throws IOException { + return blocks.containsKey(blockId); + } + + public INode retrieveINode(Path path) throws IOException { + return inodes.get(normalize(path)); + } + + public File retrieveBlock(Block block, long byteRangeStart) throws IOException { + byte[] data = blocks.get(block.getId()); + File file = createTempFile(); + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(data, (int) byteRangeStart, data.length - (int) byteRangeStart); + } finally { + if (out != null) { + out.close(); + } + } + return file; + } + + private File createTempFile() throws IOException { + File dir = new File(conf.get("fs.s3.buffer.dir")); + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException("Cannot create S3 buffer directory: " + dir); + } + File result = File.createTempFile("test-", ".tmp", dir); + result.deleteOnExit(); + return result; + } + + public Set listSubPaths(Path path) throws IOException { + Path normalizedPath = normalize(path); + // This is inefficient but more than adequate for testing purposes. + Set subPaths = new LinkedHashSet(); + for (Path p : inodes.tailMap(normalizedPath).keySet()) { + if (normalizedPath.equals(p.getParent())) { + subPaths.add(p); + } + } + return subPaths; + } + + public Set listDeepSubPaths(Path path) throws IOException { + Path normalizedPath = normalize(path); + String pathString = normalizedPath.toUri().getPath(); + if (!pathString.endsWith("/")) { + pathString += "/"; + } + // This is inefficient but more than adequate for testing purposes. + Set subPaths = new LinkedHashSet(); + for (Path p : inodes.tailMap(normalizedPath).keySet()) { + if (p.toUri().getPath().startsWith(pathString)) { + subPaths.add(p); + } + } + return subPaths; + } + + public void storeINode(Path path, INode inode) throws IOException { + inodes.put(normalize(path), inode); + } + + public void storeBlock(Block block, File file) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int numRead; + BufferedInputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(file)); + while ((numRead = in.read(buf)) >= 0) { + out.write(buf, 0, numRead); + } + } finally { + if (in != null) { + in.close(); + } + } + blocks.put(block.getId(), out.toByteArray()); + } + + private Path normalize(Path path) { + if (!path.isAbsolute()) { + throw new IllegalArgumentException("Path must be absolute: " + path); + } + return new Path(path.toUri().getPath()); + } + + public void purge() throws IOException { + inodes.clear(); + blocks.clear(); + } + + public void dump() throws IOException { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append(", \n"); + for (Map.Entry entry : inodes.entrySet()) { + sb.append(entry.getKey()).append("\n"); + INode inode = entry.getValue(); + sb.append("\t").append(inode.getFileType()).append("\n"); + if (inode.getFileType() == FileType.DIRECTORY) { + continue; + } + for (int j = 0; j < inode.getBlocks().length; j++) { + sb.append("\t").append(inode.getBlocks()[j]).append("\n"); + } + } + System.out.println(sb); + + System.out.println(inodes.keySet()); + System.out.println(blocks.keySet()); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3/Jets3tS3FileSystemContractTest.java b/src/test/org/apache/hadoop/fs/s3/Jets3tS3FileSystemContractTest.java new file mode 100644 index 00000000000..53b3c03c414 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/Jets3tS3FileSystemContractTest.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3; + +import java.io.IOException; + +public class Jets3tS3FileSystemContractTest + extends S3FileSystemContractBaseTest { + + @Override + FileSystemStore getFileSystemStore() throws IOException { + return new Jets3tFileSystemStore(); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3/S3FileSystemContractBaseTest.java b/src/test/org/apache/hadoop/fs/s3/S3FileSystemContractBaseTest.java new file mode 100644 index 00000000000..8d6744a12a3 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/S3FileSystemContractBaseTest.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3; + +import java.io.IOException; +import java.net.URI; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystemContractBaseTest; + +public abstract class S3FileSystemContractBaseTest + extends FileSystemContractBaseTest { + + private FileSystemStore store; + + abstract FileSystemStore getFileSystemStore() throws IOException; + + @Override + protected void setUp() throws Exception { + Configuration conf = new Configuration(); + store = getFileSystemStore(); + fs = new S3FileSystem(store); + fs.initialize(URI.create(conf.get("test.fs.s3.name")), conf); + } + + @Override + protected void tearDown() throws Exception { + store.purge(); + super.tearDown(); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3/TestINode.java b/src/test/org/apache/hadoop/fs/s3/TestINode.java new file mode 100644 index 00000000000..086a43eabca --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/TestINode.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.apache.hadoop.fs.s3.INode.FileType; + +public class TestINode extends TestCase { + + public void testSerializeFileWithSingleBlock() throws IOException { + Block[] blocks = { new Block(849282477840258181L, 128L) }; + INode inode = new INode(FileType.FILE, blocks); + + assertEquals("Length", 1L + 4 + 16, inode.getSerializedLength()); + InputStream in = inode.serialize(); + + INode deserialized = INode.deserialize(in); + + assertEquals("FileType", inode.getFileType(), deserialized.getFileType()); + Block[] deserializedBlocks = deserialized.getBlocks(); + assertEquals("Length", 1, deserializedBlocks.length); + assertEquals("Id", blocks[0].getId(), deserializedBlocks[0].getId()); + assertEquals("Length", blocks[0].getLength(), deserializedBlocks[0] + .getLength()); + + } + + public void testSerializeDirectory() throws IOException { + INode inode = INode.DIRECTORY_INODE; + assertEquals("Length", 1L, inode.getSerializedLength()); + InputStream in = inode.serialize(); + INode deserialized = INode.deserialize(in); + assertSame(INode.DIRECTORY_INODE, deserialized); + } + + public void testDeserializeNull() throws IOException { + assertNull(INode.deserialize(null)); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3/TestInMemoryS3FileSystemContract.java b/src/test/org/apache/hadoop/fs/s3/TestInMemoryS3FileSystemContract.java new file mode 100644 index 00000000000..5d66cf12c85 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/TestInMemoryS3FileSystemContract.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3; + +import java.io.IOException; + +public class TestInMemoryS3FileSystemContract + extends S3FileSystemContractBaseTest { + + @Override + FileSystemStore getFileSystemStore() throws IOException { + return new InMemoryFileSystemStore(); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3/TestS3Credentials.java b/src/test/org/apache/hadoop/fs/s3/TestS3Credentials.java new file mode 100644 index 00000000000..bcbf0dc607a --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/TestS3Credentials.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.fs.s3; + +import java.net.URI; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +public class TestS3Credentials extends TestCase { + public void testInvalidHostnameWithUnderscores() throws Exception { + S3Credentials s3Credentials = new S3Credentials(); + try { + s3Credentials.initialize(new URI("s3://a:b@c_d"), new Configuration()); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid hostname in URI s3://a:b@c_d", e.getMessage()); + } + } +} diff --git a/src/test/org/apache/hadoop/fs/s3/TestS3FileSystem.java b/src/test/org/apache/hadoop/fs/s3/TestS3FileSystem.java new file mode 100644 index 00000000000..f21989c5d97 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3/TestS3FileSystem.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3; + +import java.io.IOException; +import java.net.URI; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +public class TestS3FileSystem extends TestCase { + + public void testInitialization() throws IOException { + initializationTest("s3://a:b@c", "s3://a:b@c"); + initializationTest("s3://a:b@c/", "s3://a:b@c"); + initializationTest("s3://a:b@c/path", "s3://a:b@c"); + initializationTest("s3://a@c", "s3://a@c"); + initializationTest("s3://a@c/", "s3://a@c"); + initializationTest("s3://a@c/path", "s3://a@c"); + initializationTest("s3://c", "s3://c"); + initializationTest("s3://c/", "s3://c"); + initializationTest("s3://c/path", "s3://c"); + } + + private void initializationTest(String initializationUri, String expectedUri) + throws IOException { + + S3FileSystem fs = new S3FileSystem(new InMemoryFileSystemStore()); + fs.initialize(URI.create(initializationUri), new Configuration()); + assertEquals(URI.create(expectedUri), fs.getUri()); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3native/InMemoryNativeFileSystemStore.java b/src/test/org/apache/hadoop/fs/s3native/InMemoryNativeFileSystemStore.java new file mode 100644 index 00000000000..d3086da9e82 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3native/InMemoryNativeFileSystemStore.java @@ -0,0 +1,198 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3native; + +import static org.apache.hadoop.fs.s3native.NativeS3FileSystem.PATH_DELIMITER; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Map.Entry; + +import org.apache.hadoop.conf.Configuration; + +/** + *

+ * A stub implementation of {@link NativeFileSystemStore} for testing + * {@link NativeS3FileSystem} without actually connecting to S3. + *

+ */ +class InMemoryNativeFileSystemStore implements NativeFileSystemStore { + + private Configuration conf; + + private SortedMap metadataMap = + new TreeMap(); + private SortedMap dataMap = new TreeMap(); + + public void initialize(URI uri, Configuration conf) throws IOException { + this.conf = conf; + } + + public void storeEmptyFile(String key) throws IOException { + metadataMap.put(key, new FileMetadata(key, 0, System.currentTimeMillis())); + dataMap.put(key, new byte[0]); + } + + public void storeFile(String key, File file, byte[] md5Hash) + throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int numRead; + BufferedInputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(file)); + while ((numRead = in.read(buf)) >= 0) { + out.write(buf, 0, numRead); + } + } finally { + if (in != null) { + in.close(); + } + } + metadataMap.put(key, + new FileMetadata(key, file.length(), System.currentTimeMillis())); + dataMap.put(key, out.toByteArray()); + } + + public InputStream retrieve(String key) throws IOException { + return retrieve(key, 0); + } + + public InputStream retrieve(String key, long byteRangeStart) + throws IOException { + + byte[] data = dataMap.get(key); + File file = createTempFile(); + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(data, (int) byteRangeStart, + data.length - (int) byteRangeStart); + } finally { + if (out != null) { + out.close(); + } + } + return new FileInputStream(file); + } + + private File createTempFile() throws IOException { + File dir = new File(conf.get("fs.s3.buffer.dir")); + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException("Cannot create S3 buffer directory: " + dir); + } + File result = File.createTempFile("test-", ".tmp", dir); + result.deleteOnExit(); + return result; + } + + public FileMetadata retrieveMetadata(String key) throws IOException { + return metadataMap.get(key); + } + + public PartialListing list(String prefix, int maxListingLength) + throws IOException { + return list(prefix, maxListingLength, null); + } + + public PartialListing list(String prefix, int maxListingLength, + String priorLastKey) throws IOException { + + return list(prefix, PATH_DELIMITER, maxListingLength, priorLastKey); + } + + public PartialListing listAll(String prefix, int maxListingLength, + String priorLastKey) throws IOException { + + return list(prefix, null, maxListingLength, priorLastKey); + } + + private PartialListing list(String prefix, String delimiter, + int maxListingLength, String priorLastKey) throws IOException { + + if (prefix.length() > 0 && !prefix.endsWith(PATH_DELIMITER)) { + prefix += PATH_DELIMITER; + } + + List metadata = new ArrayList(); + SortedSet commonPrefixes = new TreeSet(); + for (String key : dataMap.keySet()) { + if (key.startsWith(prefix)) { + if (delimiter == null) { + metadata.add(retrieveMetadata(key)); + } else { + int delimIndex = key.indexOf(delimiter, prefix.length()); + if (delimIndex == -1) { + metadata.add(retrieveMetadata(key)); + } else { + String commonPrefix = key.substring(0, delimIndex); + commonPrefixes.add(commonPrefix); + } + } + } + if (metadata.size() + commonPrefixes.size() == maxListingLength) { + new PartialListing(key, metadata.toArray(new FileMetadata[0]), + commonPrefixes.toArray(new String[0])); + } + } + return new PartialListing(null, metadata.toArray(new FileMetadata[0]), + commonPrefixes.toArray(new String[0])); + } + + public void delete(String key) throws IOException { + metadataMap.remove(key); + dataMap.remove(key); + } + + public void rename(String srcKey, String dstKey) throws IOException { + metadataMap.put(dstKey, metadataMap.remove(srcKey)); + dataMap.put(dstKey, dataMap.remove(srcKey)); + } + + public void purge(String prefix) throws IOException { + Iterator> i = + metadataMap.entrySet().iterator(); + while (i.hasNext()) { + Entry entry = i.next(); + if (entry.getKey().startsWith(prefix)) { + dataMap.remove(entry.getKey()); + i.remove(); + } + } + } + + public void dump() throws IOException { + System.out.println(metadataMap.values()); + System.out.println(dataMap.keySet()); + } +} diff --git a/src/test/org/apache/hadoop/fs/s3native/Jets3tNativeS3FileSystemContractTest.java b/src/test/org/apache/hadoop/fs/s3native/Jets3tNativeS3FileSystemContractTest.java new file mode 100644 index 00000000000..6516c836f88 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3native/Jets3tNativeS3FileSystemContractTest.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3native; + +import java.io.IOException; + +public class Jets3tNativeS3FileSystemContractTest + extends NativeS3FileSystemContractBaseTest { + + @Override + NativeFileSystemStore getNativeFileSystemStore() throws IOException { + return new Jets3tNativeFileSystemStore(); + } +} diff --git a/src/test/org/apache/hadoop/fs/s3native/NativeS3FileSystemContractBaseTest.java b/src/test/org/apache/hadoop/fs/s3native/NativeS3FileSystemContractBaseTest.java new file mode 100644 index 00000000000..bf2e3c3d387 --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3native/NativeS3FileSystemContractBaseTest.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3native; + +import java.io.IOException; +import java.net.URI; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystemContractBaseTest; +import org.apache.hadoop.fs.Path; + +public abstract class NativeS3FileSystemContractBaseTest + extends FileSystemContractBaseTest { + + private NativeFileSystemStore store; + + abstract NativeFileSystemStore getNativeFileSystemStore() throws IOException; + + @Override + protected void setUp() throws Exception { + Configuration conf = new Configuration(); + store = getNativeFileSystemStore(); + fs = new NativeS3FileSystem(store); + fs.initialize(URI.create(conf.get("test.fs.s3n.name")), conf); + } + + @Override + protected void tearDown() throws Exception { + store.purge("test"); + super.tearDown(); + } + + public void testListStatusForRoot() throws Exception { + Path testDir = path("/test"); + assertTrue(fs.mkdirs(testDir)); + + FileStatus[] paths = fs.listStatus(path("/")); + assertEquals(1, paths.length); + assertEquals(path("/test"), paths[0].getPath()); + } + +} diff --git a/src/test/org/apache/hadoop/fs/s3native/TestInMemoryNativeS3FileSystemContract.java b/src/test/org/apache/hadoop/fs/s3native/TestInMemoryNativeS3FileSystemContract.java new file mode 100644 index 00000000000..664d39e6f4f --- /dev/null +++ b/src/test/org/apache/hadoop/fs/s3native/TestInMemoryNativeS3FileSystemContract.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3native; + +import java.io.IOException; + +public class TestInMemoryNativeS3FileSystemContract + extends NativeS3FileSystemContractBaseTest { + + @Override + NativeFileSystemStore getNativeFileSystemStore() throws IOException { + return new InMemoryNativeFileSystemStore(); + } +} diff --git a/src/test/org/apache/hadoop/http/TestGlobalFilter.java b/src/test/org/apache/hadoop/http/TestGlobalFilter.java new file mode 100644 index 00000000000..51ab60697f2 --- /dev/null +++ b/src/test/org/apache/hadoop/http/TestGlobalFilter.java @@ -0,0 +1,139 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.Set; +import java.util.TreeSet; + +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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; + +public class TestGlobalFilter extends junit.framework.TestCase { + static final Log LOG = LogFactory.getLog(HttpServer.class); + static final Set RECORDS = new TreeSet(); + + /** A very simple filter that records accessed uri's */ + static public class RecordingFilter implements Filter { + private FilterConfig filterConfig = null; + + public void init(FilterConfig filterConfig) { + this.filterConfig = filterConfig; + } + + public void destroy() { + this.filterConfig = null; + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (filterConfig == null) + return; + + String uri = ((HttpServletRequest)request).getRequestURI(); + LOG.info("filtering " + uri); + RECORDS.add(uri); + chain.doFilter(request, response); + } + + /** Configuration for RecordingFilter */ + static public class Initializer extends FilterInitializer { + public Initializer() {} + + void initFilter(FilterContainer container) { + container.addGlobalFilter("recording", RecordingFilter.class.getName(), null); + } + } + } + + + /** access a url, ignoring some IOException such as the page does not exist */ + static void access(String urlstring) throws IOException { + LOG.warn("access " + urlstring); + URL url = new URL(urlstring); + URLConnection connection = url.openConnection(); + connection.connect(); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader( + connection.getInputStream())); + try { + for(; in.readLine() != null; ); + } finally { + in.close(); + } + } catch(IOException ioe) { + LOG.warn("urlstring=" + urlstring, ioe); + } + } + + public void testServletFilter() throws Exception { + Configuration conf = new Configuration(); + + //start a http server with CountingFilter + conf.set(HttpServer.FILTER_INITIALIZER_PROPERTY, + RecordingFilter.Initializer.class.getName()); + HttpServer http = new HttpServer("datanode", "localhost", 0, true, conf); + http.start(); + + final String fsckURL = "/fsck"; + final String stacksURL = "/stacks"; + final String ajspURL = "/a.jsp"; + final String listPathsURL = "/listPaths"; + final String dataURL = "/data"; + final String streamFile = "/streamFile"; + final String rootURL = "/"; + final String allURL = "/*"; + final String outURL = "/static/a.out"; + final String logURL = "/logs/a.log"; + + final String[] urls = {fsckURL, stacksURL, ajspURL, listPathsURL, + dataURL, streamFile, rootURL, allURL, outURL, logURL}; + + //access the urls + final String prefix = "http://localhost:" + http.getPort(); + try { + for(int i = 0; i < urls.length; i++) { + access(prefix + urls[i]); + } + } finally { + http.stop(); + } + + LOG.info("RECORDS = " + RECORDS); + + //verify records + for(int i = 0; i < urls.length; i++) { + assertTrue(RECORDS.remove(urls[i])); + } + assertTrue(RECORDS.isEmpty()); + } +} diff --git a/src/test/org/apache/hadoop/http/TestServletFilter.java b/src/test/org/apache/hadoop/http/TestServletFilter.java new file mode 100644 index 00000000000..8052f9ad492 --- /dev/null +++ b/src/test/org/apache/hadoop/http/TestServletFilter.java @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.Random; + +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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; + +public class TestServletFilter extends junit.framework.TestCase { + static final Log LOG = LogFactory.getLog(HttpServer.class); + static volatile String uri = null; + + /** A very simple filter which record the uri filtered. */ + static public class SimpleFilter implements Filter { + private FilterConfig filterConfig = null; + + public void init(FilterConfig filterConfig) { + this.filterConfig = filterConfig; + } + + public void destroy() { + this.filterConfig = null; + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (filterConfig == null) + return; + + uri = ((HttpServletRequest)request).getRequestURI(); + LOG.info("filtering " + uri); + chain.doFilter(request, response); + } + + /** Configuration for the filter */ + static public class Initializer extends FilterInitializer { + public Initializer() {} + + void initFilter(FilterContainer container) { + container.addFilter("simple", SimpleFilter.class.getName(), null); + } + } + } + + + /** access a url, ignoring some IOException such as the page does not exist */ + static void access(String urlstring) throws IOException { + LOG.warn("access " + urlstring); + URL url = new URL(urlstring); + URLConnection connection = url.openConnection(); + connection.connect(); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader( + connection.getInputStream())); + try { + for(; in.readLine() != null; ); + } finally { + in.close(); + } + } catch(IOException ioe) { + LOG.warn("urlstring=" + urlstring, ioe); + } + } + + public void testServletFilter() throws Exception { + Configuration conf = new Configuration(); + + //start a http server with CountingFilter + conf.set(HttpServer.FILTER_INITIALIZER_PROPERTY, + SimpleFilter.Initializer.class.getName()); + HttpServer http = new HttpServer("datanode", "localhost", 0, true, conf); + http.start(); + + final String fsckURL = "/fsck"; + final String stacksURL = "/stacks"; + final String ajspURL = "/a.jsp"; + final String logURL = "/logs/a.log"; + final String hadooplogoURL = "/static/hadoop-logo.jpg"; + + final String[] urls = {fsckURL, stacksURL, ajspURL, logURL, hadooplogoURL}; + final Random ran = new Random(); + final int[] sequence = new int[50]; + + //generate a random sequence and update counts + for(int i = 0; i < sequence.length; i++) { + sequence[i] = ran.nextInt(urls.length); + } + + //access the urls as the sequence + final String prefix = "http://localhost:" + http.getPort(); + try { + for(int i = 0; i < sequence.length; i++) { + access(prefix + urls[sequence[i]]); + + //make sure everything except fsck get filtered + if (sequence[i] == 0) { + assertEquals(null, uri); + } else { + assertEquals(urls[sequence[i]], uri); + uri = null; + } + } + } finally { + http.stop(); + } + } +} diff --git a/src/test/org/apache/hadoop/io/RandomDatum.java b/src/test/org/apache/hadoop/io/RandomDatum.java new file mode 100644 index 00000000000..ab8f34febab --- /dev/null +++ b/src/test/org/apache/hadoop/io/RandomDatum.java @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.util.*; +import java.io.*; + +public class RandomDatum implements WritableComparable { + private int length; + private byte[] data; + + public RandomDatum() {} + + public RandomDatum(Random random) { + length = 10 + (int) Math.pow(10.0, random.nextFloat() * 3.0); + data = new byte[length]; + random.nextBytes(data); + } + + public int getLength() { + return length; + } + + public void write(DataOutput out) throws IOException { + out.writeInt(length); + out.write(data); + } + + public void readFields(DataInput in) throws IOException { + length = in.readInt(); + if (data == null || length > data.length) + data = new byte[length]; + in.readFully(data, 0, length); + } + + public int compareTo(Object o) { + RandomDatum that = (RandomDatum)o; + return WritableComparator.compareBytes(this.data, 0, this.length, + that.data, 0, that.length); + } + + public boolean equals(Object o) { + return compareTo(o) == 0; + } + + private static final char[] HEX_DIGITS = + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + /** Returns a string representation of this object. */ + public String toString() { + StringBuffer buf = new StringBuffer(length*2); + for (int i = 0; i < length; i++) { + int b = data[i]; + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + return buf.toString(); + } + + public static class Generator { + Random random; + + private RandomDatum key; + private RandomDatum value; + + public Generator() { random = new Random(); } + public Generator(int seed) { random = new Random(seed); } + + public RandomDatum getKey() { return key; } + public RandomDatum getValue() { return value; } + + public void next() { + key = new RandomDatum(random); + value = new RandomDatum(random); + } + } + + /** A WritableComparator optimized for RandomDatum. */ + public static class Comparator extends WritableComparator { + public Comparator() { + super(RandomDatum.class); + } + + public int compare(byte[] b1, int s1, int l1, + byte[] b2, int s2, int l2) { + int n1 = readInt(b1, s1); + int n2 = readInt(b2, s2); + return compareBytes(b1, s1+4, n1, b2, s2+4, n2); + } + } + +} diff --git a/src/test/org/apache/hadoop/io/TestArrayFile.java b/src/test/org/apache/hadoop/io/TestArrayFile.java new file mode 100644 index 00000000000..f279bd74319 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestArrayFile.java @@ -0,0 +1,155 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.*; +import junit.framework.TestCase; + +import org.apache.commons.logging.*; + +import org.apache.hadoop.fs.*; +import org.apache.hadoop.conf.*; + +/** Support for flat files of binary key/value pairs. */ +public class TestArrayFile extends TestCase { + private static final Log LOG = LogFactory.getLog(TestArrayFile.class); + private static String FILE = + System.getProperty("test.build.data",".") + "/test.array"; + + public TestArrayFile(String name) { + super(name); + } + + public void testArrayFile() throws Exception { + Configuration conf = new Configuration(); + FileSystem fs = FileSystem.getLocal(conf); + RandomDatum[] data = generate(10000); + writeTest(fs, data, FILE); + readTest(fs, data, FILE, conf); + } + + public void testEmptyFile() throws Exception { + Configuration conf = new Configuration(); + FileSystem fs = FileSystem.getLocal(conf); + writeTest(fs, new RandomDatum[0], FILE); + ArrayFile.Reader reader = new ArrayFile.Reader(fs, FILE, conf); + assertNull(reader.get(0, new RandomDatum())); + reader.close(); + } + + private static RandomDatum[] generate(int count) { + LOG.debug("generating " + count + " records in debug"); + RandomDatum[] data = new RandomDatum[count]; + RandomDatum.Generator generator = new RandomDatum.Generator(); + for (int i = 0; i < count; i++) { + generator.next(); + data[i] = generator.getValue(); + } + return data; + } + + private static void writeTest(FileSystem fs, RandomDatum[] data, String file) + throws IOException { + Configuration conf = new Configuration(); + MapFile.delete(fs, file); + LOG.debug("creating with " + data.length + " debug"); + ArrayFile.Writer writer = new ArrayFile.Writer(conf, fs, file, RandomDatum.class); + writer.setIndexInterval(100); + for (int i = 0; i < data.length; i++) + writer.append(data[i]); + writer.close(); + } + + private static void readTest(FileSystem fs, RandomDatum[] data, String file, Configuration conf) + throws IOException { + RandomDatum v = new RandomDatum(); + LOG.debug("reading " + data.length + " debug"); + ArrayFile.Reader reader = new ArrayFile.Reader(fs, file, conf); + for (int i = 0; i < data.length; i++) { // try forwards + reader.get(i, v); + if (!v.equals(data[i])) { + throw new RuntimeException("wrong value at " + i); + } + } + for (int i = data.length-1; i >= 0; i--) { // then backwards + reader.get(i, v); + if (!v.equals(data[i])) { + throw new RuntimeException("wrong value at " + i); + } + } + reader.close(); + LOG.debug("done reading " + data.length + " debug"); + } + + + /** For debugging and testing. */ + public static void main(String[] args) throws Exception { + int count = 1024 * 1024; + boolean create = true; + boolean check = true; + String file = FILE; + String usage = "Usage: TestArrayFile [-count N] [-nocreate] [-nocheck] file"; + + if (args.length == 0) { + System.err.println(usage); + System.exit(-1); + } + + Configuration conf = new Configuration(); + int i = 0; + Path fpath = null; + FileSystem fs = null; + try { + for (; i < args.length; i++) { // parse command line + if (args[i] == null) { + continue; + } else if (args[i].equals("-count")) { + count = Integer.parseInt(args[++i]); + } else if (args[i].equals("-nocreate")) { + create = false; + } else if (args[i].equals("-nocheck")) { + check = false; + } else { + // file is required parameter + file = args[i]; + fpath=new Path(file); + } + } + + fs = fpath.getFileSystem(conf); + + LOG.info("count = " + count); + LOG.info("create = " + create); + LOG.info("check = " + check); + LOG.info("file = " + file); + + RandomDatum[] data = generate(count); + + if (create) { + writeTest(fs, data, file); + } + + if (check) { + readTest(fs, data, file, conf); + } + } finally { + fs.close(); + } + } +} diff --git a/src/test/org/apache/hadoop/io/TestArrayWritable.java b/src/test/org/apache/hadoop/io/TestArrayWritable.java new file mode 100644 index 00000000000..47d0ce9f635 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestArrayWritable.java @@ -0,0 +1,64 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.*; + +import junit.framework.TestCase; + +/** Unit tests for ArrayWritable */ +public class TestArrayWritable extends TestCase { + + static class TextArrayWritable extends ArrayWritable { + public TextArrayWritable() { + super(Text.class); + } + } + + public TestArrayWritable(String name) { + super(name); + } + + /** + * If valueClass is undefined, readFields should throw an exception indicating + * that the field is null. Otherwise, readFields should succeed. + */ + public void testThrowUndefinedValueException() throws IOException { + // Get a buffer containing a simple text array + Text[] elements = {new Text("zero"), new Text("one"), new Text("two")}; + TextArrayWritable sourceArray = new TextArrayWritable(); + sourceArray.set(elements); + + // Write it to a normal output buffer + DataOutputBuffer out = new DataOutputBuffer(); + DataInputBuffer in = new DataInputBuffer(); + sourceArray.write(out); + + // Read the output buffer with TextReadable. Since the valueClass is defined, + // this should succeed + TextArrayWritable destArray = new TextArrayWritable(); + in.reset(out.getData(), out.getLength()); + destArray.readFields(in); + Writable[] destElements = destArray.get(); + assertTrue(destElements.length == elements.length); + for (int i = 0; i < elements.length; i++) { + assertEquals(destElements[i],elements[i]); + } + } +} diff --git a/src/test/org/apache/hadoop/io/TestBloomMapFile.java b/src/test/org/apache/hadoop/io/TestBloomMapFile.java new file mode 100644 index 00000000000..2a7d22455f6 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestBloomMapFile.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +import junit.framework.TestCase; + +public class TestBloomMapFile extends TestCase { + private static Configuration conf = new Configuration(); + + public void testMembershipTest() throws Exception { + // write the file + Path dirName = new Path(System.getProperty("test.build.data",".") + + getName() + ".bloommapfile"); + FileSystem fs = FileSystem.getLocal(conf); + Path qualifiedDirName = fs.makeQualified(dirName); + conf.setInt("io.mapfile.bloom.size", 2048); + BloomMapFile.Writer writer = new BloomMapFile.Writer(conf, fs, + qualifiedDirName.toString(), IntWritable.class, Text.class); + IntWritable key = new IntWritable(); + Text value = new Text(); + for (int i = 0; i < 2000; i += 2) { + key.set(i); + value.set("00" + i); + writer.append(key, value); + } + writer.close(); + + BloomMapFile.Reader reader = new BloomMapFile.Reader(fs, + qualifiedDirName.toString(), conf); + // check false positives rate + int falsePos = 0; + int falseNeg = 0; + for (int i = 0; i < 2000; i++) { + key.set(i); + boolean exists = reader.probablyHasKey(key); + if (i % 2 == 0) { + if (!exists) falseNeg++; + } else { + if (exists) falsePos++; + } + } + reader.close(); + fs.delete(qualifiedDirName, true); + System.out.println("False negatives: " + falseNeg); + assertEquals(0, falseNeg); + System.out.println("False positives: " + falsePos); + assertTrue(falsePos < 2); + } + +} diff --git a/src/test/org/apache/hadoop/io/TestBytesWritable.java b/src/test/org/apache/hadoop/io/TestBytesWritable.java new file mode 100644 index 00000000000..35e0d0ed827 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestBytesWritable.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.io; + +import junit.framework.TestCase; + +/** + * This is the unit test for BytesWritable. + */ +public class TestBytesWritable extends TestCase { + + public void testSizeChange() throws Exception { + byte[] hadoop = "hadoop".getBytes(); + BytesWritable buf = new BytesWritable(hadoop); + int size = buf.getLength(); + int orig_capacity = buf.getCapacity(); + buf.setSize(size*2); + int new_capacity = buf.getCapacity(); + System.arraycopy(buf.getBytes(), 0, buf.getBytes(), size, size); + assertTrue(new_capacity >= size * 2); + assertEquals(size * 2, buf.getLength()); + assertTrue(new_capacity != orig_capacity); + buf.setSize(size*4); + assertTrue(new_capacity != buf.getCapacity()); + for(int i=0; i < size*2; ++i) { + assertEquals(hadoop[i%size], buf.getBytes()[i]); + } + // shrink the buffer + buf.setCapacity(1); + // make sure the size has been cut down too + assertEquals(1, buf.getLength()); + // but that the data is still there + assertEquals(hadoop[0], buf.getBytes()[0]); + } + + public void testHash() throws Exception { + byte[] owen = "owen".getBytes(); + BytesWritable buf = new BytesWritable(owen); + assertEquals(4347922, buf.hashCode()); + buf.setCapacity(10000); + assertEquals(4347922, buf.hashCode()); + buf.setSize(0); + assertEquals(1, buf.hashCode()); + } + + public void testCompare() throws Exception { + byte[][] values = new byte[][]{"abc".getBytes(), + "ad".getBytes(), + "abcd".getBytes(), + "".getBytes(), + "b".getBytes()}; + BytesWritable[] buf = new BytesWritable[values.length]; + for(int i=0; i < values.length; ++i) { + buf[i] = new BytesWritable(values[i]); + } + // check to make sure the compare function is symetric and reflexive + for(int i=0; i < values.length; ++i) { + for(int j=0; j < values.length; ++j) { + assertTrue(buf[i].compareTo(buf[j]) == -buf[j].compareTo(buf[i])); + assertTrue((i == j) == (buf[i].compareTo(buf[j]) == 0)); + } + } + assertTrue(buf[0].compareTo(buf[1]) < 0); + assertTrue(buf[1].compareTo(buf[2]) > 0); + assertTrue(buf[2].compareTo(buf[3]) > 0); + assertTrue(buf[3].compareTo(buf[4]) < 0); + } + + private void checkToString(byte[] input, String expected) { + String actual = new BytesWritable(input).toString(); + assertEquals(expected, actual); + } + + public void testToString() { + checkToString(new byte[]{0,1,2,0x10}, "00 01 02 10"); + checkToString(new byte[]{-0x80, -0x7f, -0x1, -0x2, 1, 0}, + "80 81 ff fe 01 00"); + } +} + diff --git a/src/test/org/apache/hadoop/io/TestDefaultStringifier.java b/src/test/org/apache/hadoop/io/TestDefaultStringifier.java new file mode 100644 index 00000000000..c96cc732938 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestDefaultStringifier.java @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.IOException; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; + +public class TestDefaultStringifier extends TestCase { + + private static Configuration conf = new Configuration(); + private static final Log LOG = LogFactory.getLog(TestDefaultStringifier.class); + + private char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + + public void testWithWritable() throws Exception { + + conf.set("io.serializations", "org.apache.hadoop.io.serializer.WritableSerialization"); + + LOG.info("Testing DefaultStringifier with Text"); + + Random random = new Random(); + + //test with a Text + for(int i=0;i<10;i++) { + //generate a random string + StringBuilder builder = new StringBuilder(); + int strLen = random.nextInt(40); + for(int j=0; j< strLen; j++) { + builder.append(alphabet[random.nextInt(alphabet.length)]); + } + Text text = new Text(builder.toString()); + DefaultStringifier stringifier = new DefaultStringifier(conf, Text.class); + + String str = stringifier.toString(text); + Text claimedText = stringifier.fromString(str); + LOG.info("Object: " + text); + LOG.info("String representation of the object: " + str); + assertEquals(text, claimedText); + } + } + + public void testWithJavaSerialization() throws Exception { + conf.set("io.serializations", "org.apache.hadoop.io.serializer.JavaSerialization"); + + LOG.info("Testing DefaultStringifier with Serializable Integer"); + + //Integer implements Serializable + Integer testInt = Integer.valueOf(42); + DefaultStringifier stringifier = new DefaultStringifier(conf, Integer.class); + + String str = stringifier.toString(testInt); + Integer claimedInt = stringifier.fromString(str); + LOG.info("String representation of the object: " + str); + + assertEquals(testInt, claimedInt); + } + + public void testStoreLoad() throws IOException { + + LOG.info("Testing DefaultStringifier#store() and #load()"); + conf.set("io.serializations", "org.apache.hadoop.io.serializer.WritableSerialization"); + Text text = new Text("uninteresting test string"); + String keyName = "test.defaultstringifier.key1"; + + DefaultStringifier.store(conf,text, keyName); + + Text claimedText = DefaultStringifier.load(conf, keyName, Text.class); + assertEquals("DefaultStringifier#load() or #store() might be flawed" + , text, claimedText); + + } + + public void testStoreLoadArray() throws IOException { + LOG.info("Testing DefaultStringifier#storeArray() and #loadArray()"); + conf.set("io.serializations", "org.apache.hadoop.io.serializer.JavaSerialization"); + + String keyName = "test.defaultstringifier.key2"; + + Integer[] array = new Integer[] {1,2,3,4,5}; + + + DefaultStringifier.storeArray(conf, array, keyName); + + Integer[] claimedArray = DefaultStringifier.loadArray(conf, keyName, Integer.class); + for (int i = 0; i < array.length; i++) { + assertEquals("two arrays are not equal", array[i], claimedArray[i]); + } + + } + +} diff --git a/src/test/org/apache/hadoop/io/TestEnumSetWritable.java b/src/test/org/apache/hadoop/io/TestEnumSetWritable.java new file mode 100644 index 00000000000..a512bb1bc2d --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestEnumSetWritable.java @@ -0,0 +1,103 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.IOException; +import java.util.EnumSet; + +import junit.framework.TestCase; + +/** Unit test for EnumSetWritable */ +public class TestEnumSetWritable extends TestCase { + + enum TestEnumSet { + CREATE, OVERWRITE, APPEND; + } + + EnumSet nonEmptyFlag = EnumSet.of(TestEnumSet.APPEND); + EnumSetWritable nonEmptyFlagWritable = new EnumSetWritable( + nonEmptyFlag); + + @SuppressWarnings("unchecked") + public void testSerializeAndDeserializeNonEmpty() throws IOException { + DataOutputBuffer out = new DataOutputBuffer(); + ObjectWritable.writeObject(out, nonEmptyFlagWritable, nonEmptyFlagWritable + .getClass(), null); + DataInputBuffer in = new DataInputBuffer(); + in.reset(out.getData(), out.getLength()); + EnumSet read = ((EnumSetWritable) ObjectWritable + .readObject(in, null)).get(); + assertEquals(read, nonEmptyFlag); + } + + EnumSet emptyFlag = EnumSet.noneOf(TestEnumSet.class); + + @SuppressWarnings("unchecked") + public void testSerializeAndDeserializeEmpty() throws IOException { + + boolean gotException = false; + try { + new EnumSetWritable(emptyFlag); + } catch (RuntimeException e) { + gotException = true; + } + + assertTrue( + "Instantiate empty EnumSetWritable with no element type class providesd should throw exception.", + gotException); + + EnumSetWritable emptyFlagWritable = new EnumSetWritable( + emptyFlag, TestEnumSet.class); + DataOutputBuffer out = new DataOutputBuffer(); + ObjectWritable.writeObject(out, emptyFlagWritable, emptyFlagWritable + .getClass(), null); + DataInputBuffer in = new DataInputBuffer(); + in.reset(out.getData(), out.getLength()); + EnumSet read = ((EnumSetWritable) ObjectWritable + .readObject(in, null)).get(); + assertEquals(read, emptyFlag); + } + + @SuppressWarnings("unchecked") + public void testSerializeAndDeserializeNull() throws IOException { + + boolean gotException = false; + try { + new EnumSetWritable(null); + } catch (RuntimeException e) { + gotException = true; + } + + assertTrue( + "Instantiate empty EnumSetWritable with no element type class providesd should throw exception.", + gotException); + + EnumSetWritable nullFlagWritable = new EnumSetWritable( + null, TestEnumSet.class); + + DataOutputBuffer out = new DataOutputBuffer(); + ObjectWritable.writeObject(out, nullFlagWritable, nullFlagWritable + .getClass(), null); + DataInputBuffer in = new DataInputBuffer(); + in.reset(out.getData(), out.getLength()); + EnumSet read = ((EnumSetWritable) ObjectWritable + .readObject(in, null)).get(); + assertEquals(read, null); + } +} diff --git a/src/test/org/apache/hadoop/io/TestGenericWritable.java b/src/test/org/apache/hadoop/io/TestGenericWritable.java new file mode 100644 index 00000000000..486d93d4385 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestGenericWritable.java @@ -0,0 +1,178 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configurable; +import org.apache.hadoop.conf.Configuration; + +/** + * TestCase for {@link GenericWritable} class. + * @see TestWritable#testWritable(Writable) + */ +public class TestGenericWritable extends TestCase { + + private Configuration conf; + public static final String CONF_TEST_KEY = "test.generic.writable"; + public static final String CONF_TEST_VALUE = "dummy"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + conf = new Configuration(); + //set the configuration parameter + conf.set(CONF_TEST_KEY, CONF_TEST_VALUE); + } + + /** Dummy class for testing {@link GenericWritable} */ + public static class Foo implements Writable { + private String foo = "foo"; + public void readFields(DataInput in) throws IOException { + foo = Text.readString(in); + } + public void write(DataOutput out) throws IOException { + Text.writeString(out, foo); + } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Foo)) + return false; + return this.foo.equals(((Foo)obj).foo); + } + } + /** Dummy class for testing {@link GenericWritable} */ + public static class Bar implements Writable, Configurable { + private int bar = 42; //The Answer to The Ultimate Question Of Life, the Universe and Everything + private Configuration conf = null; + public void readFields(DataInput in) throws IOException { + bar = in.readInt(); + } + public void write(DataOutput out) throws IOException { + out.writeInt(bar); + } + public Configuration getConf() { + return conf; + } + public void setConf(Configuration conf) { + this.conf = conf; + } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Bar)) + return false; + return this.bar == ((Bar)obj).bar; + } + } + + /** Dummy class for testing {@link GenericWritable} */ + public static class Baz extends Bar { + @Override + public void readFields(DataInput in) throws IOException { + super.readFields(in); + //needs a configuration parameter + assertEquals("Configuration is not set for the wrapped object", + CONF_TEST_VALUE, getConf().get(CONF_TEST_KEY)); + } + @Override + public void write(DataOutput out) throws IOException { + super.write(out); + } + } + + /** Dummy class for testing {@link GenericWritable} */ + public static class FooGenericWritable extends GenericWritable { + @Override + @SuppressWarnings("unchecked") + protected Class[] getTypes() { + return new Class[] {Foo.class, Bar.class, Baz.class}; + } + @Override + public boolean equals(Object obj) { + if(! (obj instanceof FooGenericWritable)) + return false; + return get().equals(((FooGenericWritable)obj).get()); + } + } + + public void testFooWritable() throws Exception { + System.out.println("Testing Writable wrapped in GenericWritable"); + FooGenericWritable generic = new FooGenericWritable(); + generic.setConf(conf); + Foo foo = new Foo(); + generic.set(foo); + TestWritable.testWritable(generic); + } + + public void testBarWritable() throws Exception { + System.out.println("Testing Writable, Configurable wrapped in GenericWritable"); + FooGenericWritable generic = new FooGenericWritable(); + generic.setConf(conf); + Bar bar = new Bar(); + bar.setConf(conf); + generic.set(bar); + + //test writing generic writable + FooGenericWritable after + = (FooGenericWritable)TestWritable.testWritable(generic, conf); + + //test configuration + System.out.println("Testing if Configuration is passed to wrapped classes"); + assertTrue(after.get() instanceof Configurable); + assertNotNull(((Configurable)after.get()).getConf()); + } + + public void testBazWritable() throws Exception { + System.out.println("Testing for GenericWritable to find class names"); + FooGenericWritable generic = new FooGenericWritable(); + generic.setConf(conf); + Baz baz = new Baz(); + generic.set(baz); + TestWritable.testWritable(generic, conf); + } + + public void testSet() throws Exception { + Foo foo = new Foo(); + FooGenericWritable generic = new FooGenericWritable(); + //exception should not occur + generic.set(foo); + + try { + //exception should occur, since IntWritable is not registered + generic = new FooGenericWritable(); + generic.set(new IntWritable(1)); + fail("Generic writable should have thrown an exception for a Writable not registered"); + }catch (RuntimeException e) { + //ignore + } + + } + + public void testGet() throws Exception { + Foo foo = new Foo(); + FooGenericWritable generic = new FooGenericWritable(); + generic.set(foo); + assertEquals(foo, generic.get()); + } + +} diff --git a/src/test/org/apache/hadoop/io/TestMD5Hash.java b/src/test/org/apache/hadoop/io/TestMD5Hash.java new file mode 100644 index 00000000000..feb1107ed46 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestMD5Hash.java @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import org.apache.hadoop.io.TestWritable; +import junit.framework.TestCase; +import java.security.MessageDigest; +import java.util.Random; + +/** Unit tests for MD5Hash. */ +public class TestMD5Hash extends TestCase { + public TestMD5Hash(String name) { super(name); } + + private static final Random RANDOM = new Random(); + + public static MD5Hash getTestHash() throws Exception { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] buffer = new byte[1024]; + RANDOM.nextBytes(buffer); + digest.update(buffer); + return new MD5Hash(digest.digest()); + } + + protected static byte[] D00 = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + protected static byte[] DFF = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + public void testMD5Hash() throws Exception { + MD5Hash md5Hash = getTestHash(); + + final MD5Hash md5Hash00 + = new MD5Hash(D00); + + final MD5Hash md5HashFF + = new MD5Hash(DFF); + + MD5Hash orderedHash = new MD5Hash(new byte[]{1,2,3,4,5,6,7,8,9,10,11,12, + 13,14,15,16}); + MD5Hash backwardHash = new MD5Hash(new byte[]{-1,-2,-3,-4,-5,-6,-7,-8, + -9,-10,-11,-12, -13, -14, + -15,-16}); + MD5Hash closeHash1 = new MD5Hash(new byte[]{-1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0}); + MD5Hash closeHash2 = new MD5Hash(new byte[]{-1,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0}); + + // test i/o + TestWritable.testWritable(md5Hash); + TestWritable.testWritable(md5Hash00); + TestWritable.testWritable(md5HashFF); + + // test equals() + assertEquals(md5Hash, md5Hash); + assertEquals(md5Hash00, md5Hash00); + assertEquals(md5HashFF, md5HashFF); + + // test compareTo() + assertTrue(md5Hash.compareTo(md5Hash) == 0); + assertTrue(md5Hash00.compareTo(md5Hash) < 0); + assertTrue(md5HashFF.compareTo(md5Hash) > 0); + + // test toString and string ctor + assertEquals(md5Hash, new MD5Hash(md5Hash.toString())); + assertEquals(md5Hash00, new MD5Hash(md5Hash00.toString())); + assertEquals(md5HashFF, new MD5Hash(md5HashFF.toString())); + + assertEquals(0x01020304, orderedHash.quarterDigest()); + assertEquals(0xfffefdfc, backwardHash.quarterDigest()); + + assertEquals(0x0102030405060708L, orderedHash.halfDigest()); + assertEquals(0xfffefdfcfbfaf9f8L, backwardHash.halfDigest()); + assertTrue("hash collision", + closeHash1.hashCode() != closeHash2.hashCode()); + + Thread t1 = new Thread() { + public void run() { + for (int i = 0; i < 100; i++) { + MD5Hash hash = new MD5Hash(DFF); + assertEquals(hash, md5HashFF); + } + } + }; + + Thread t2 = new Thread() { + public void run() { + for (int i = 0; i < 100; i++) { + MD5Hash hash = new MD5Hash(D00); + assertEquals(hash, md5Hash00); + } + } + }; + + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + } + +} diff --git a/src/test/org/apache/hadoop/io/TestMapFile.java b/src/test/org/apache/hadoop/io/TestMapFile.java new file mode 100644 index 00000000000..f006d4f4013 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestMapFile.java @@ -0,0 +1,124 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.io; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +import junit.framework.TestCase; + +public class TestMapFile extends TestCase { + private static Configuration conf = new Configuration(); + + /** + * Test getClosest feature. + * @throws Exception + */ + public void testGetClosest() throws Exception { + // Write a mapfile of simple data: keys are + Path dirName = new Path(System.getProperty("test.build.data",".") + + getName() + ".mapfile"); + FileSystem fs = FileSystem.getLocal(conf); + Path qualifiedDirName = fs.makeQualified(dirName); + // Make an index entry for every third insertion. + MapFile.Writer.setIndexInterval(conf, 3); + MapFile.Writer writer = new MapFile.Writer(conf, fs, + qualifiedDirName.toString(), Text.class, Text.class); + // Assert that the index interval is 1 + assertEquals(3, writer.getIndexInterval()); + // Add entries up to 100 in intervals of ten. + final int FIRST_KEY = 10; + for (int i = FIRST_KEY; i < 100; i += 10) { + String iStr = Integer.toString(i); + Text t = new Text("00".substring(iStr.length()) + iStr); + writer.append(t, t); + } + writer.close(); + // Now do getClosest on created mapfile. + MapFile.Reader reader = new MapFile.Reader(fs, qualifiedDirName.toString(), + conf); + Text key = new Text("55"); + Text value = new Text(); + Text closest = (Text)reader.getClosest(key, value); + // Assert that closest after 55 is 60 + assertEquals(new Text("60"), closest); + // Get closest that falls before the passed key: 50 + closest = (Text)reader.getClosest(key, value, true); + assertEquals(new Text("50"), closest); + // Test get closest when we pass explicit key + final Text TWENTY = new Text("20"); + closest = (Text)reader.getClosest(TWENTY, value); + assertEquals(TWENTY, closest); + closest = (Text)reader.getClosest(TWENTY, value, true); + assertEquals(TWENTY, closest); + // Test what happens at boundaries. Assert if searching a key that is + // less than first key in the mapfile, that the first key is returned. + key = new Text("00"); + closest = (Text)reader.getClosest(key, value); + assertEquals(FIRST_KEY, Integer.parseInt(closest.toString())); + + // If we're looking for the first key before, and we pass in a key before + // the first key in the file, we should get null + closest = (Text)reader.getClosest(key, value, true); + assertNull(closest); + + // Assert that null is returned if key is > last entry in mapfile. + key = new Text("99"); + closest = (Text)reader.getClosest(key, value); + assertNull(closest); + + // If we were looking for the key before, we should get the last key + closest = (Text)reader.getClosest(key, value, true); + assertEquals(new Text("90"), closest); + } + + public void testMidKey() throws Exception { + // Write a mapfile of simple data: keys are + Path dirName = new Path(System.getProperty("test.build.data",".") + + getName() + ".mapfile"); + FileSystem fs = FileSystem.getLocal(conf); + Path qualifiedDirName = fs.makeQualified(dirName); + + MapFile.Writer writer = new MapFile.Writer(conf, fs, + qualifiedDirName.toString(), IntWritable.class, IntWritable.class); + writer.append(new IntWritable(1), new IntWritable(1)); + writer.close(); + // Now do getClosest on created mapfile. + MapFile.Reader reader = new MapFile.Reader(fs, qualifiedDirName.toString(), + conf); + assertEquals(new IntWritable(1), reader.midKey()); + } + + + public void testMidKeyEmpty() throws Exception { + // Write a mapfile of simple data: keys are + Path dirName = new Path(System.getProperty("test.build.data",".") + + getName() + ".mapfile"); + FileSystem fs = FileSystem.getLocal(conf); + Path qualifiedDirName = fs.makeQualified(dirName); + + MapFile.Writer writer = new MapFile.Writer(conf, fs, + qualifiedDirName.toString(), IntWritable.class, IntWritable.class); + writer.close(); + // Now do getClosest on created mapfile. + MapFile.Reader reader = new MapFile.Reader(fs, qualifiedDirName.toString(), + conf); + assertEquals(null, reader.midKey()); + } +} diff --git a/src/test/org/apache/hadoop/io/TestMapWritable.java b/src/test/org/apache/hadoop/io/TestMapWritable.java new file mode 100644 index 00000000000..3d8c4ab3c20 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestMapWritable.java @@ -0,0 +1,132 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.io; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Tests MapWritable + */ +public class TestMapWritable extends TestCase { + /** the test */ + @SuppressWarnings("unchecked") + public void testMapWritable() { + Text[] keys = { + new Text("key1"), + new Text("key2"), + new Text("Key3"), + }; + + BytesWritable[] values = { + new BytesWritable("value1".getBytes()), + new BytesWritable("value2".getBytes()), + new BytesWritable("value3".getBytes()) + }; + + MapWritable inMap = new MapWritable(); + for (int i = 0; i < keys.length; i++) { + inMap.put(keys[i], values[i]); + } + + MapWritable outMap = new MapWritable(inMap); + assertEquals(inMap.size(), outMap.size()); + + for (Map.Entry e: inMap.entrySet()) { + assertTrue(outMap.containsKey(e.getKey())); + assertEquals(0, ((WritableComparable) outMap.get(e.getKey())).compareTo( + e.getValue())); + } + + // Now for something a little harder... + + Text[] maps = { + new Text("map1"), + new Text("map2") + }; + + MapWritable mapOfMaps = new MapWritable(); + mapOfMaps.put(maps[0], inMap); + mapOfMaps.put(maps[1], outMap); + + MapWritable copyOfMapOfMaps = new MapWritable(mapOfMaps); + for (int i = 0; i < maps.length; i++) { + assertTrue(copyOfMapOfMaps.containsKey(maps[i])); + MapWritable a = (MapWritable) mapOfMaps.get(maps[i]); + MapWritable b = (MapWritable) copyOfMapOfMaps.get(maps[i]); + assertEquals(a.size(), b.size()); + for (Writable key: a.keySet()) { + assertTrue(b.containsKey(key)); + + // This will work because we know what we put into each set + + WritableComparable aValue = (WritableComparable) a.get(key); + WritableComparable bValue = (WritableComparable) b.get(key); + assertEquals(0, aValue.compareTo(bValue)); + } + } + } + + /** + * Test that number of "unknown" classes is propagated across multiple copies. + */ + @SuppressWarnings("deprecation") + public void testForeignClass() { + MapWritable inMap = new MapWritable(); + inMap.put(new Text("key"), new UTF8("value")); + inMap.put(new Text("key2"), new UTF8("value2")); + MapWritable outMap = new MapWritable(inMap); + MapWritable copyOfCopy = new MapWritable(outMap); + assertEquals(1, copyOfCopy.getNewClasses()); + } + + /** + * Assert MapWritable does not grow across calls to readFields. + * @throws Exception + * @see HADOOP-2244 + */ + public void testMultipleCallsToReadFieldsAreSafe() throws Exception { + // Create an instance and add a key/value. + MapWritable m = new MapWritable(); + final Text t = new Text(getName()); + m.put(t, t); + // Get current size of map. Key values are 't'. + int count = m.size(); + // Now serialize... save off the bytes. + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + m.write(dos); + dos.close(); + // Now add new values to the MapWritable. + m.put(new Text("key1"), new Text("value1")); + m.put(new Text("key2"), new Text("value2")); + // Now deserialize the original MapWritable. Ensure count and key values + // match original state. + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + DataInputStream dis = new DataInputStream(bais); + m.readFields(dis); + assertEquals(count, m.size()); + assertTrue(m.get(t).equals(t)); + dis.close(); + } +} diff --git a/src/test/org/apache/hadoop/io/TestSequenceFileSerialization.java b/src/test/org/apache/hadoop/io/TestSequenceFileSerialization.java new file mode 100644 index 00000000000..c9fc1eae4f5 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestSequenceFileSerialization.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.SequenceFile.Reader; +import org.apache.hadoop.io.SequenceFile.Writer; + +public class TestSequenceFileSerialization extends TestCase { + + private Configuration conf; + private FileSystem fs; + + @Override + protected void setUp() throws Exception { + conf = new Configuration(); + conf.set("io.serializations", + "org.apache.hadoop.io.serializer.JavaSerialization"); + fs = FileSystem.getLocal(conf); + } + + @Override + protected void tearDown() throws Exception { + fs.close(); + } + + public void testJavaSerialization() throws Exception { + Path file = new Path(System.getProperty("test.build.data",".") + + "/test.seq"); + + fs.delete(file, true); + Writer writer = SequenceFile.createWriter(fs, conf, file, Long.class, + String.class); + + writer.append(1L, "one"); + writer.append(2L, "two"); + + writer.close(); + + Reader reader = new Reader(fs, file, conf); + assertEquals(1L, reader.next((Object) null)); + assertEquals("one", reader.getCurrentValue((Object) null)); + assertEquals(2L, reader.next((Object) null)); + assertEquals("two", reader.getCurrentValue((Object) null)); + assertNull(reader.next((Object) null)); + reader.close(); + + } +} diff --git a/src/test/org/apache/hadoop/io/TestSetFile.java b/src/test/org/apache/hadoop/io/TestSetFile.java new file mode 100644 index 00000000000..70d02e013f0 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestSetFile.java @@ -0,0 +1,157 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.*; +import java.util.*; +import junit.framework.TestCase; + +import org.apache.commons.logging.*; + +import org.apache.hadoop.fs.*; +import org.apache.hadoop.conf.*; +import org.apache.hadoop.io.SequenceFile.CompressionType; + +/** Support for flat files of binary key/value pairs. */ +public class TestSetFile extends TestCase { + private static final Log LOG = LogFactory.getLog(TestSetFile.class); + private static String FILE = + System.getProperty("test.build.data",".") + "/test.set"; + + private static Configuration conf = new Configuration(); + + public TestSetFile(String name) { super(name); } + + public void testSetFile() throws Exception { + FileSystem fs = FileSystem.getLocal(conf); + try { + RandomDatum[] data = generate(10000); + writeTest(fs, data, FILE, CompressionType.NONE); + readTest(fs, data, FILE); + + writeTest(fs, data, FILE, CompressionType.BLOCK); + readTest(fs, data, FILE); + } finally { + fs.close(); + } + } + + private static RandomDatum[] generate(int count) { + LOG.info("generating " + count + " records in memory"); + RandomDatum[] data = new RandomDatum[count]; + RandomDatum.Generator generator = new RandomDatum.Generator(); + for (int i = 0; i < count; i++) { + generator.next(); + data[i] = generator.getValue(); + } + LOG.info("sorting " + count + " records"); + Arrays.sort(data); + return data; + } + + private static void writeTest(FileSystem fs, RandomDatum[] data, + String file, CompressionType compress) + throws IOException { + MapFile.delete(fs, file); + LOG.info("creating with " + data.length + " records"); + SetFile.Writer writer = + new SetFile.Writer(conf, fs, file, + WritableComparator.get(RandomDatum.class), + compress); + for (int i = 0; i < data.length; i++) + writer.append(data[i]); + writer.close(); + } + + private static void readTest(FileSystem fs, RandomDatum[] data, String file) + throws IOException { + RandomDatum v = new RandomDatum(); + int sample = (int)Math.sqrt(data.length); + Random random = new Random(); + LOG.info("reading " + sample + " records"); + SetFile.Reader reader = new SetFile.Reader(fs, file, conf); + for (int i = 0; i < sample; i++) { + if (!reader.seek(data[random.nextInt(data.length)])) + throw new RuntimeException("wrong value at " + i); + } + reader.close(); + LOG.info("done reading " + data.length); + } + + + /** For debugging and testing. */ + public static void main(String[] args) throws Exception { + int count = 1024 * 1024; + boolean create = true; + boolean check = true; + String file = FILE; + String compress = "NONE"; + + String usage = "Usage: TestSetFile [-count N] [-nocreate] [-nocheck] [-compress type] file"; + + if (args.length == 0) { + System.err.println(usage); + System.exit(-1); + } + + int i = 0; + Path fpath=null; + FileSystem fs = null; + try { + for (; i < args.length; i++) { // parse command line + if (args[i] == null) { + continue; + } else if (args[i].equals("-count")) { + count = Integer.parseInt(args[++i]); + } else if (args[i].equals("-nocreate")) { + create = false; + } else if (args[i].equals("-nocheck")) { + check = false; + } else if (args[i].equals("-compress")) { + compress = args[++i]; + } else { + // file is required parameter + file = args[i]; + fpath=new Path(file); + } + } + + fs = fpath.getFileSystem(conf); + + LOG.info("count = " + count); + LOG.info("create = " + create); + LOG.info("check = " + check); + LOG.info("compress = " + compress); + LOG.info("file = " + file); + + RandomDatum[] data = generate(count); + + if (create) { + writeTest(fs, data, file, CompressionType.valueOf(compress)); + } + + if (check) { + readTest(fs, data, file); + } + + } finally { + fs.close(); + } + } +} diff --git a/src/test/org/apache/hadoop/io/TestSortedMapWritable.java b/src/test/org/apache/hadoop/io/TestSortedMapWritable.java new file mode 100644 index 00000000000..927bfc1f42d --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestSortedMapWritable.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.io; + +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Tests SortedMapWritable + */ +public class TestSortedMapWritable extends TestCase { + /** the test */ + @SuppressWarnings("unchecked") + public void testSortedMapWritable() { + Text[] keys = { + new Text("key1"), + new Text("key2"), + new Text("key3"), + }; + + BytesWritable[] values = { + new BytesWritable("value1".getBytes()), + new BytesWritable("value2".getBytes()), + new BytesWritable("value3".getBytes()) + }; + + SortedMapWritable inMap = new SortedMapWritable(); + for (int i = 0; i < keys.length; i++) { + inMap.put(keys[i], values[i]); + } + + assertEquals(0, inMap.firstKey().compareTo(keys[0])); + assertEquals(0, inMap.lastKey().compareTo(keys[2])); + + SortedMapWritable outMap = new SortedMapWritable(inMap); + assertEquals(inMap.size(), outMap.size()); + + for (Map.Entry e: inMap.entrySet()) { + assertTrue(outMap.containsKey(e.getKey())); + assertEquals(0, ((WritableComparable) outMap.get(e.getKey())).compareTo( + e.getValue())); + } + + // Now for something a little harder... + + Text[] maps = { + new Text("map1"), + new Text("map2") + }; + + SortedMapWritable mapOfMaps = new SortedMapWritable(); + mapOfMaps.put(maps[0], inMap); + mapOfMaps.put(maps[1], outMap); + + SortedMapWritable copyOfMapOfMaps = new SortedMapWritable(mapOfMaps); + for (int i = 0; i < maps.length; i++) { + assertTrue(copyOfMapOfMaps.containsKey(maps[i])); + + SortedMapWritable a = (SortedMapWritable) mapOfMaps.get(maps[i]); + SortedMapWritable b = (SortedMapWritable) copyOfMapOfMaps.get(maps[i]); + assertEquals(a.size(), b.size()); + for (Writable key: a.keySet()) { + assertTrue(b.containsKey(key)); + + // This will work because we know what we put into each set + + WritableComparable aValue = (WritableComparable) a.get(key); + WritableComparable bValue = (WritableComparable) b.get(key); + assertEquals(0, aValue.compareTo(bValue)); + } + } + } + + /** + * Test that number of "unknown" classes is propagated across multiple copies. + */ + @SuppressWarnings("deprecation") + public void testForeignClass() { + SortedMapWritable inMap = new SortedMapWritable(); + inMap.put(new Text("key"), new UTF8("value")); + inMap.put(new Text("key2"), new UTF8("value2")); + SortedMapWritable outMap = new SortedMapWritable(inMap); + SortedMapWritable copyOfCopy = new SortedMapWritable(outMap); + assertEquals(1, copyOfCopy.getNewClasses()); + } +} diff --git a/src/test/org/apache/hadoop/io/TestText.java b/src/test/org/apache/hadoop/io/TestText.java new file mode 100644 index 00000000000..6e004860991 --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestText.java @@ -0,0 +1,266 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import junit.framework.TestCase; + +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.util.Random; + +/** Unit tests for LargeUTF8. */ +public class TestText extends TestCase { + private static final int NUM_ITERATIONS = 100; + public TestText(String name) { super(name); } + + private static final Random RANDOM = new Random(1); + + private static final int RAND_LEN = -1; + + // generate a valid java String + private static String getTestString(int len) throws Exception { + StringBuffer buffer = new StringBuffer(); + int length = (len==RAND_LEN) ? RANDOM.nextInt(1000) : len; + while (buffer.length() test = WritableName.getClass("long",conf); + assertTrue(test != null); + } + + public void testSetName() throws Exception { + Configuration conf = new Configuration(); + WritableName.setName(SimpleWritable.class, testName); + + Class test = WritableName.getClass(testName,conf); + assertTrue(test.equals(SimpleWritable.class)); + } + + + public void testAddName() throws Exception { + Configuration conf = new Configuration(); + String altName = testName + ".alt"; + + WritableName.addName(SimpleWritable.class, altName); + + Class test = WritableName.getClass(altName, conf); + assertTrue(test.equals(SimpleWritable.class)); + + // check original name still works + test = WritableName.getClass(testName, conf); + assertTrue(test.equals(SimpleWritable.class)); + + } + + public void testBadName() throws Exception { + Configuration conf = new Configuration(); + try { + Class test = WritableName.getClass("unknown_junk",conf); + assertTrue(false); + } catch(IOException e) { + assertTrue(e.getMessage().matches(".*unknown_junk.*")); + } + } + +} diff --git a/src/test/org/apache/hadoop/io/TestWritableUtils.java b/src/test/org/apache/hadoop/io/TestWritableUtils.java new file mode 100644 index 00000000000..2487fc0612c --- /dev/null +++ b/src/test/org/apache/hadoop/io/TestWritableUtils.java @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import junit.framework.TestCase; + +public class TestWritableUtils extends TestCase { + private static final Log LOG = LogFactory.getLog(TestWritableUtils.class); + + public static void testValue(int val, int vintlen) throws IOException { + DataOutputBuffer buf = new DataOutputBuffer(); + DataInputBuffer inbuf = new DataInputBuffer(); + WritableUtils.writeVInt(buf, val); + if (LOG.isDebugEnabled()) { + LOG.debug("Value = " + val); + BytesWritable printer = new BytesWritable(); + printer.set(buf.getData(), 0, buf.getLength()); + LOG.debug("Buffer = " + printer); + } + inbuf.reset(buf.getData(), 0, buf.getLength()); + assertEquals(val, WritableUtils.readVInt(inbuf)); + assertEquals(vintlen, buf.getLength()); + assertEquals(vintlen, WritableUtils.getVIntSize(val)); + assertEquals(vintlen, WritableUtils.decodeVIntSize(buf.getData()[0])); + } + + public static void testVInt() throws Exception { + testValue(12, 1); + testValue(127, 1); + testValue(-112, 1); + testValue(-113, 2); + testValue(-128, 2); + testValue(128, 2); + testValue(-129, 2); + testValue(255, 2); + testValue(-256, 2); + testValue(256, 3); + testValue(-257, 3); + testValue(65535, 3); + testValue(-65536, 3); + testValue(65536, 4); + testValue(-65537, 4); + } +} diff --git a/src/test/org/apache/hadoop/io/compress/TestCodec.java b/src/test/org/apache/hadoop/io/compress/TestCodec.java new file mode 100644 index 00000000000..38e4a358376 --- /dev/null +++ b/src/test/org/apache/hadoop/io/compress/TestCodec.java @@ -0,0 +1,249 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.io.compress; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.DataInputBuffer; +import org.apache.hadoop.io.DataOutputBuffer; +import org.apache.hadoop.io.RandomDatum; +import org.apache.hadoop.io.SequenceFile; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.Writable; +import org.apache.hadoop.util.ReflectionUtils; +import org.apache.hadoop.io.SequenceFile.CompressionType; +import org.apache.hadoop.io.compress.CompressionOutputStream; +import org.apache.hadoop.io.compress.zlib.ZlibFactory; + +public class TestCodec extends TestCase { + + private static final Log LOG= + LogFactory.getLog(TestCodec.class); + + private Configuration conf = new Configuration(); + private int count = 10000; + private int seed = new Random().nextInt(); + + public void testDefaultCodec() throws IOException { + codecTest(conf, seed, 0, "org.apache.hadoop.io.compress.DefaultCodec"); + codecTest(conf, seed, count, "org.apache.hadoop.io.compress.DefaultCodec"); + } + + public void testGzipCodec() throws IOException { + codecTest(conf, seed, 0, "org.apache.hadoop.io.compress.GzipCodec"); + codecTest(conf, seed, count, "org.apache.hadoop.io.compress.GzipCodec"); + } + + public void testBZip2Codec() throws IOException { + codecTest(conf, seed, 0, "org.apache.hadoop.io.compress.BZip2Codec"); + codecTest(conf, seed, count, "org.apache.hadoop.io.compress.BZip2Codec"); + } + + private static void codecTest(Configuration conf, int seed, int count, + String codecClass) + throws IOException { + + // Create the codec + CompressionCodec codec = null; + try { + codec = (CompressionCodec) + ReflectionUtils.newInstance(conf.getClassByName(codecClass), conf); + } catch (ClassNotFoundException cnfe) { + throw new IOException("Illegal codec!"); + } + LOG.info("Created a Codec object of type: " + codecClass); + + // Generate data + DataOutputBuffer data = new DataOutputBuffer(); + RandomDatum.Generator generator = new RandomDatum.Generator(seed); + for(int i=0; i < count; ++i) { + generator.next(); + RandomDatum key = generator.getKey(); + RandomDatum value = generator.getValue(); + + key.write(data); + value.write(data); + } + DataInputBuffer originalData = new DataInputBuffer(); + DataInputStream originalIn = new DataInputStream(new BufferedInputStream(originalData)); + originalData.reset(data.getData(), 0, data.getLength()); + + LOG.info("Generated " + count + " records"); + + // Compress data + DataOutputBuffer compressedDataBuffer = new DataOutputBuffer(); + CompressionOutputStream deflateFilter = + codec.createOutputStream(compressedDataBuffer); + DataOutputStream deflateOut = + new DataOutputStream(new BufferedOutputStream(deflateFilter)); + deflateOut.write(data.getData(), 0, data.getLength()); + deflateOut.flush(); + deflateFilter.finish(); + LOG.info("Finished compressing data"); + + // De-compress data + DataInputBuffer deCompressedDataBuffer = new DataInputBuffer(); + deCompressedDataBuffer.reset(compressedDataBuffer.getData(), 0, + compressedDataBuffer.getLength()); + CompressionInputStream inflateFilter = + codec.createInputStream(deCompressedDataBuffer); + DataInputStream inflateIn = + new DataInputStream(new BufferedInputStream(inflateFilter)); + + // Check + for(int i=0; i < count; ++i) { + RandomDatum k1 = new RandomDatum(); + RandomDatum v1 = new RandomDatum(); + k1.readFields(originalIn); + v1.readFields(originalIn); + + RandomDatum k2 = new RandomDatum(); + RandomDatum v2 = new RandomDatum(); + k2.readFields(inflateIn); + v2.readFields(inflateIn); + } + LOG.info("SUCCESS! Completed checking " + count + " records"); + } + + public void testCodecPoolGzipReuse() throws Exception { + Configuration conf = new Configuration(); + conf.setBoolean("hadoop.native.lib", true); + if (!ZlibFactory.isNativeZlibLoaded(conf)) { + LOG.warn("testCodecPoolGzipReuse skipped: native libs not loaded"); + return; + } + GzipCodec gzc = ReflectionUtils.newInstance(GzipCodec.class, conf); + DefaultCodec dfc = ReflectionUtils.newInstance(DefaultCodec.class, conf); + Compressor c1 = CodecPool.getCompressor(gzc); + Compressor c2 = CodecPool.getCompressor(dfc); + CodecPool.returnCompressor(c1); + CodecPool.returnCompressor(c2); + assertTrue("Got mismatched ZlibCompressor", c2 != CodecPool.getCompressor(gzc)); + } + + public void testSequenceFileDefaultCodec() throws IOException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + sequenceFileCodecTest(conf, 100, "org.apache.hadoop.io.compress.DefaultCodec", 100); + sequenceFileCodecTest(conf, 200000, "org.apache.hadoop.io.compress.DefaultCodec", 1000000); + } + + public void testSequenceFileBZip2Codec() throws IOException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + sequenceFileCodecTest(conf, 0, "org.apache.hadoop.io.compress.BZip2Codec", 100); + sequenceFileCodecTest(conf, 100, "org.apache.hadoop.io.compress.BZip2Codec", 100); + sequenceFileCodecTest(conf, 200000, "org.apache.hadoop.io.compress.BZip2Codec", 1000000); + } + + private static void sequenceFileCodecTest(Configuration conf, int lines, + String codecClass, int blockSize) + throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { + + Path filePath = new Path("SequenceFileCodecTest." + codecClass); + // Configuration + conf.setInt("io.seqfile.compress.blocksize", blockSize); + + // Create the SequenceFile + FileSystem fs = FileSystem.get(conf); + LOG.info("Creating SequenceFile with codec \"" + codecClass + "\""); + SequenceFile.Writer writer = SequenceFile.createWriter(fs, conf, filePath, + Text.class, Text.class, CompressionType.BLOCK, + (CompressionCodec)Class.forName(codecClass).newInstance()); + + // Write some data + LOG.info("Writing to SequenceFile..."); + for (int i=0; i getCompressorType() { + return null; + } + + public Compressor createCompressor() { + return null; + } + + public CompressionInputStream createInputStream(InputStream in, + Decompressor decompressor) + throws IOException { + return null; + } + + public CompressionInputStream createInputStream(InputStream in) + throws IOException { + return null; + } + + public CompressionOutputStream createOutputStream(OutputStream out, + Compressor compressor) + throws IOException { + return null; + } + + public Class getDecompressorType() { + return null; + } + + public Decompressor createDecompressor() { + return null; + } + + public String getDefaultExtension() { + return ".base"; + } + } + + private static class BarCodec extends BaseCodec { + public String getDefaultExtension() { + return "bar"; + } + } + + private static class FooBarCodec extends BaseCodec { + public String getDefaultExtension() { + return ".foo.bar"; + } + } + + private static class FooCodec extends BaseCodec { + public String getDefaultExtension() { + return ".foo"; + } + } + + /** + * Returns a factory for a given set of codecs + * @param classes the codec classes to include + * @return a new factory + */ + private static CompressionCodecFactory setClasses(Class[] classes) { + Configuration conf = new Configuration(); + CompressionCodecFactory.setCodecClasses(conf, Arrays.asList(classes)); + return new CompressionCodecFactory(conf); + } + + private static void checkCodec(String msg, + Class expected, CompressionCodec actual) { + assertEquals(msg + " unexpected codec found", + expected.getName(), + actual.getClass().getName()); + } + + public static void testFinding() { + CompressionCodecFactory factory = + new CompressionCodecFactory(new Configuration()); + CompressionCodec codec = factory.getCodec(new Path("/tmp/foo.bar")); + assertEquals("default factory foo codec", null, codec); + codec = factory.getCodec(new Path("/tmp/foo.gz")); + checkCodec("default factory for .gz", GzipCodec.class, codec); + codec = factory.getCodec(new Path("/tmp/foo.bz2")); + checkCodec("default factory for .bz2", BZip2Codec.class, codec); + factory = setClasses(new Class[0]); + codec = factory.getCodec(new Path("/tmp/foo.bar")); + assertEquals("empty codec bar codec", null, codec); + codec = factory.getCodec(new Path("/tmp/foo.gz")); + assertEquals("empty codec gz codec", null, codec); + codec = factory.getCodec(new Path("/tmp/foo.bz2")); + assertEquals("default factory for .bz2", null, codec); + factory = setClasses(new Class[]{BarCodec.class, FooCodec.class, + FooBarCodec.class}); + codec = factory.getCodec(new Path("/tmp/.foo.bar.gz")); + assertEquals("full factory gz codec", null, codec); + codec = factory.getCodec(new Path("/tmp/foo.bz2")); + assertEquals("default factory for .bz2", null, codec); + codec = factory.getCodec(new Path("/tmp/foo.bar")); + checkCodec("full factory bar codec", BarCodec.class, codec); + codec = factory.getCodec(new Path("/tmp/foo/baz.foo.bar")); + checkCodec("full factory foo bar codec", FooBarCodec.class, codec); + codec = factory.getCodec(new Path("/tmp/foo.foo")); + checkCodec("full factory foo codec", FooCodec.class, codec); + } +} diff --git a/src/test/org/apache/hadoop/io/retry/TestRetryProxy.java b/src/test/org/apache/hadoop/io/retry/TestRetryProxy.java new file mode 100644 index 00000000000..c48e87b7dd9 --- /dev/null +++ b/src/test/org/apache/hadoop/io/retry/TestRetryProxy.java @@ -0,0 +1,170 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io.retry; + +import static org.apache.hadoop.io.retry.RetryPolicies.RETRY_FOREVER; +import static org.apache.hadoop.io.retry.RetryPolicies.TRY_ONCE_DONT_FAIL; +import static org.apache.hadoop.io.retry.RetryPolicies.TRY_ONCE_THEN_FAIL; +import static org.apache.hadoop.io.retry.RetryPolicies.retryByException; +import static org.apache.hadoop.io.retry.RetryPolicies.retryByRemoteException; +import static org.apache.hadoop.io.retry.RetryPolicies.retryUpToMaximumCountWithFixedSleep; +import static org.apache.hadoop.io.retry.RetryPolicies.retryUpToMaximumCountWithProportionalSleep; +import static org.apache.hadoop.io.retry.RetryPolicies.retryUpToMaximumTimeWithFixedSleep; +import static org.apache.hadoop.io.retry.RetryPolicies.exponentialBackoffRetry; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import junit.framework.TestCase; + +import org.apache.hadoop.io.retry.UnreliableInterface.FatalException; +import org.apache.hadoop.io.retry.UnreliableInterface.UnreliableException; +import org.apache.hadoop.ipc.RemoteException; + +public class TestRetryProxy extends TestCase { + + private UnreliableImplementation unreliableImpl; + + @Override + protected void setUp() throws Exception { + unreliableImpl = new UnreliableImplementation(); + } + + public void testTryOnceThenFail() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, TRY_ONCE_THEN_FAIL); + unreliable.alwaysSucceeds(); + try { + unreliable.failsOnceThenSucceeds(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testTryOnceDontFail() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, TRY_ONCE_DONT_FAIL); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.failsOnceThenSucceedsWithReturnValue(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testRetryForever() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, RETRY_FOREVER); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + unreliable.failsTenTimesThenSucceeds(); + } + + public void testRetryUpToMaximumCountWithFixedSleep() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + retryUpToMaximumCountWithFixedSleep(8, 1, TimeUnit.NANOSECONDS)); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.failsTenTimesThenSucceeds(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testRetryUpToMaximumTimeWithFixedSleep() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + retryUpToMaximumTimeWithFixedSleep(80, 10, TimeUnit.NANOSECONDS)); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.failsTenTimesThenSucceeds(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testRetryUpToMaximumCountWithProportionalSleep() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + retryUpToMaximumCountWithProportionalSleep(8, 1, TimeUnit.NANOSECONDS)); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.failsTenTimesThenSucceeds(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testExponentialRetry() throws UnreliableException { + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + exponentialBackoffRetry(5, 1L, TimeUnit.NANOSECONDS)); + unreliable.alwaysSucceeds(); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.failsTenTimesThenSucceeds(); + fail("Should fail"); + } catch (UnreliableException e) { + // expected + } + } + + public void testRetryByException() throws UnreliableException { + Map, RetryPolicy> exceptionToPolicyMap = + Collections., RetryPolicy>singletonMap(FatalException.class, TRY_ONCE_THEN_FAIL); + + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + retryByException(RETRY_FOREVER, exceptionToPolicyMap)); + unreliable.failsOnceThenSucceeds(); + try { + unreliable.alwaysFailsWithFatalException(); + fail("Should fail"); + } catch (FatalException e) { + // expected + } + } + + public void testRetryByRemoteException() throws UnreliableException { + Map, RetryPolicy> exceptionToPolicyMap = + Collections., RetryPolicy>singletonMap(FatalException.class, TRY_ONCE_THEN_FAIL); + + UnreliableInterface unreliable = (UnreliableInterface) + RetryProxy.create(UnreliableInterface.class, unreliableImpl, + retryByRemoteException(RETRY_FOREVER, exceptionToPolicyMap)); + try { + unreliable.alwaysFailsWithRemoteFatalException(); + fail("Should fail"); + } catch (RemoteException e) { + // expected + } + } + +} diff --git a/src/test/org/apache/hadoop/io/retry/UnreliableImplementation.java b/src/test/org/apache/hadoop/io/retry/UnreliableImplementation.java new file mode 100644 index 00000000000..5971ee72165 --- /dev/null +++ b/src/test/org/apache/hadoop/io/retry/UnreliableImplementation.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io.retry; + +import org.apache.hadoop.ipc.RemoteException; + +public class UnreliableImplementation implements UnreliableInterface { + + private int failsOnceInvocationCount, + failsOnceWithValueInvocationCount, + failsTenTimesInvocationCount; + + public void alwaysSucceeds() { + // do nothing + } + + public void alwaysFailsWithFatalException() throws FatalException { + throw new FatalException(); + } + + public void alwaysFailsWithRemoteFatalException() throws RemoteException { + throw new RemoteException(FatalException.class.getName(), "Oops"); + } + + public void failsOnceThenSucceeds() throws UnreliableException { + if (failsOnceInvocationCount++ == 0) { + throw new UnreliableException(); + } + } + + public boolean failsOnceThenSucceedsWithReturnValue() throws UnreliableException { + if (failsOnceWithValueInvocationCount++ == 0) { + throw new UnreliableException(); + } + return true; + } + + public void failsTenTimesThenSucceeds() throws UnreliableException { + if (failsTenTimesInvocationCount++ < 10) { + throw new UnreliableException(); + } + } + +} diff --git a/src/test/org/apache/hadoop/io/retry/UnreliableInterface.java b/src/test/org/apache/hadoop/io/retry/UnreliableInterface.java new file mode 100644 index 00000000000..af4959151e7 --- /dev/null +++ b/src/test/org/apache/hadoop/io/retry/UnreliableInterface.java @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io.retry; + +import org.apache.hadoop.ipc.RemoteException; + +public interface UnreliableInterface { + + public static class UnreliableException extends Exception { + // no body + } + + public static class FatalException extends UnreliableException { + // no body + } + + void alwaysSucceeds() throws UnreliableException; + + void alwaysFailsWithFatalException() throws FatalException; + void alwaysFailsWithRemoteFatalException() throws RemoteException; + + void failsOnceThenSucceeds() throws UnreliableException; + boolean failsOnceThenSucceedsWithReturnValue() throws UnreliableException; + + void failsTenTimesThenSucceeds() throws UnreliableException; +} diff --git a/src/test/org/apache/hadoop/io/serializer/TestWritableSerialization.java b/src/test/org/apache/hadoop/io/serializer/TestWritableSerialization.java new file mode 100644 index 00000000000..6a551753245 --- /dev/null +++ b/src/test/org/apache/hadoop/io/serializer/TestWritableSerialization.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.io.serializer; + +import static org.apache.hadoop.io.TestGenericWritable.CONF_TEST_KEY; +import static org.apache.hadoop.io.TestGenericWritable.CONF_TEST_VALUE; +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.io.DataInputBuffer; +import org.apache.hadoop.io.DataOutputBuffer; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.TestGenericWritable.Baz; +import org.apache.hadoop.io.TestGenericWritable.FooGenericWritable; +import org.apache.hadoop.util.GenericsUtil; + +public class TestWritableSerialization extends TestCase { + + private static final Configuration conf = new Configuration(); + + static { + conf.set("io.serializations" + , "org.apache.hadoop.io.serializer.WritableSerialization"); + } + + public void testWritableSerialization() throws Exception { + Text before = new Text("test writable"); + testSerialization(conf, before); + } + + + public void testWritableConfigurable() throws Exception { + + //set the configuration parameter + conf.set(CONF_TEST_KEY, CONF_TEST_VALUE); + + //reuse TestGenericWritable inner classes to test + //writables that also implement Configurable. + FooGenericWritable generic = new FooGenericWritable(); + generic.setConf(conf); + Baz baz = new Baz(); + generic.set(baz); + Baz result = testSerialization(conf, baz); + assertNotNull(result.getConf()); + } + + /** + * A utility that tests serialization/deserialization. + * @param the class of the item + * @param conf configuration to use, "io.serializations" is read to + * determine the serialization + * @param before item to (de)serialize + * @return deserialized item + */ + public static K testSerialization(Configuration conf, K before) + throws Exception { + + SerializationFactory factory = new SerializationFactory(conf); + Serializer serializer + = factory.getSerializer(GenericsUtil.getClass(before)); + Deserializer deserializer + = factory.getDeserializer(GenericsUtil.getClass(before)); + + DataOutputBuffer out = new DataOutputBuffer(); + serializer.open(out); + serializer.serialize(before); + serializer.close(); + + DataInputBuffer in = new DataInputBuffer(); + in.reset(out.getData(), out.getLength()); + deserializer.open(in); + K after = deserializer.deserialize(null); + deserializer.close(); + + assertEquals(before, after); + return after; + } + +} diff --git a/src/test/org/apache/hadoop/ipc/TestIPC.java b/src/test/org/apache/hadoop/ipc/TestIPC.java new file mode 100644 index 00000000000..df5a1558153 --- /dev/null +++ b/src/test/org/apache/hadoop/ipc/TestIPC.java @@ -0,0 +1,243 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ipc; + +import org.apache.commons.logging.*; + +import org.apache.hadoop.io.Writable; +import org.apache.hadoop.io.LongWritable; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.net.NetUtils; + +import java.util.Random; +import java.io.IOException; +import java.net.InetSocketAddress; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +/** Unit tests for IPC. */ +public class TestIPC extends TestCase { + public static final Log LOG = + LogFactory.getLog(TestIPC.class); + + final private static Configuration conf = new Configuration(); + final static private int PING_INTERVAL = 1000; + + static { + Client.setPingInterval(conf, PING_INTERVAL); + } + public TestIPC(String name) { super(name); } + + private static final Random RANDOM = new Random(); + + private static final String ADDRESS = "0.0.0.0"; + + private static class TestServer extends Server { + private boolean sleep; + + public TestServer(int handlerCount, boolean sleep) + throws IOException { + super(ADDRESS, 0, LongWritable.class, handlerCount, conf); + this.sleep = sleep; + } + + @Override + public Writable call(Class protocol, Writable param, long receiveTime) + throws IOException { + if (sleep) { + try { + Thread.sleep(RANDOM.nextInt(2*PING_INTERVAL)); // sleep a bit + } catch (InterruptedException e) {} + } + return param; // echo param as result + } + } + + private static class SerialCaller extends Thread { + private Client client; + private InetSocketAddress server; + private int count; + private boolean failed; + + public SerialCaller(Client client, InetSocketAddress server, int count) { + this.client = client; + this.server = server; + this.count = count; + } + + public void run() { + for (int i = 0; i < count; i++) { + try { + LongWritable param = new LongWritable(RANDOM.nextLong()); + LongWritable value = + (LongWritable)client.call(param, server); + if (!param.equals(value)) { + LOG.fatal("Call failed!"); + failed = true; + break; + } + } catch (Exception e) { + LOG.fatal("Caught: " + StringUtils.stringifyException(e)); + failed = true; + } + } + } + } + + private static class ParallelCaller extends Thread { + private Client client; + private int count; + private InetSocketAddress[] addresses; + private boolean failed; + + public ParallelCaller(Client client, InetSocketAddress[] addresses, + int count) { + this.client = client; + this.addresses = addresses; + this.count = count; + } + + public void run() { + for (int i = 0; i < count; i++) { + try { + Writable[] params = new Writable[addresses.length]; + for (int j = 0; j < addresses.length; j++) + params[j] = new LongWritable(RANDOM.nextLong()); + Writable[] values = client.call(params, addresses); + for (int j = 0; j < addresses.length; j++) { + if (!params[j].equals(values[j])) { + LOG.fatal("Call failed!"); + failed = true; + break; + } + } + } catch (Exception e) { + LOG.fatal("Caught: " + StringUtils.stringifyException(e)); + failed = true; + } + } + } + } + + public void testSerial() throws Exception { + testSerial(3, false, 2, 5, 100); + } + + public void testSerial(int handlerCount, boolean handlerSleep, + int clientCount, int callerCount, int callCount) + throws Exception { + Server server = new TestServer(handlerCount, handlerSleep); + InetSocketAddress addr = NetUtils.getConnectAddress(server); + server.start(); + + Client[] clients = new Client[clientCount]; + for (int i = 0; i < clientCount; i++) { + clients[i] = new Client(LongWritable.class, conf); + } + + SerialCaller[] callers = new SerialCaller[callerCount]; + for (int i = 0; i < callerCount; i++) { + callers[i] = new SerialCaller(clients[i%clientCount], addr, callCount); + callers[i].start(); + } + for (int i = 0; i < callerCount; i++) { + callers[i].join(); + assertFalse(callers[i].failed); + } + for (int i = 0; i < clientCount; i++) { + clients[i].stop(); + } + server.stop(); + } + + public void testParallel() throws Exception { + testParallel(10, false, 2, 4, 2, 4, 100); + } + + public void testParallel(int handlerCount, boolean handlerSleep, + int serverCount, int addressCount, + int clientCount, int callerCount, int callCount) + throws Exception { + Server[] servers = new Server[serverCount]; + for (int i = 0; i < serverCount; i++) { + servers[i] = new TestServer(handlerCount, handlerSleep); + servers[i].start(); + } + + InetSocketAddress[] addresses = new InetSocketAddress[addressCount]; + for (int i = 0; i < addressCount; i++) { + addresses[i] = NetUtils.getConnectAddress(servers[i%serverCount]); + } + + Client[] clients = new Client[clientCount]; + for (int i = 0; i < clientCount; i++) { + clients[i] = new Client(LongWritable.class, conf); + } + + ParallelCaller[] callers = new ParallelCaller[callerCount]; + for (int i = 0; i < callerCount; i++) { + callers[i] = + new ParallelCaller(clients[i%clientCount], addresses, callCount); + callers[i].start(); + } + for (int i = 0; i < callerCount; i++) { + callers[i].join(); + assertFalse(callers[i].failed); + } + for (int i = 0; i < clientCount; i++) { + clients[i].stop(); + } + for (int i = 0; i < serverCount; i++) { + servers[i].stop(); + } + } + + public void testStandAloneClient() throws Exception { + testParallel(10, false, 2, 4, 2, 4, 100); + Client client = new Client(LongWritable.class, conf); + InetSocketAddress address = new InetSocketAddress("127.0.0.1", 10); + try { + client.call(new LongWritable(RANDOM.nextLong()), + address); + fail("Expected an exception to have been thrown"); + } catch (IOException e) { + String message = e.getMessage(); + String addressText = address.toString(); + assertTrue("Did not find "+addressText+" in "+message, + message.contains(addressText)); + Throwable cause=e.getCause(); + assertNotNull("No nested exception in "+e,cause); + String causeText=cause.getMessage(); + assertTrue("Did not find " + causeText + " in " + message, + message.contains(causeText)); + } + } + + + public static void main(String[] args) throws Exception { + + //new TestIPC("test").testSerial(5, false, 2, 10, 1000); + + new TestIPC("test").testParallel(10, false, 2, 4, 2, 4, 1000); + + } + +} diff --git a/src/test/org/apache/hadoop/ipc/TestIPCServerResponder.java b/src/test/org/apache/hadoop/ipc/TestIPCServerResponder.java new file mode 100644 index 00000000000..2591da01432 --- /dev/null +++ b/src/test/org/apache/hadoop/ipc/TestIPCServerResponder.java @@ -0,0 +1,150 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ipc; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.io.BytesWritable; +import org.apache.hadoop.io.Writable; +import org.apache.hadoop.net.NetUtils; + +/** + * This test provokes partial writes in the server, which is + * serving multiple clients. + */ +public class TestIPCServerResponder extends TestCase { + + public static final Log LOG = + LogFactory.getLog(TestIPCServerResponder.class); + + private static Configuration conf = new Configuration(); + + public TestIPCServerResponder(final String name) { + super(name); + } + + private static final Random RANDOM = new Random(); + + private static final String ADDRESS = "0.0.0.0"; + + private static final int BYTE_COUNT = 1024; + private static final byte[] BYTES = new byte[BYTE_COUNT]; + static { + for (int i = 0; i < BYTE_COUNT; i++) + BYTES[i] = (byte) ('a' + (i % 26)); + } + + private static class TestServer extends Server { + + private boolean sleep; + + public TestServer(final int handlerCount, final boolean sleep) + throws IOException { + super(ADDRESS, 0, BytesWritable.class, handlerCount, conf); + // Set the buffer size to half of the maximum parameter/result size + // to force the socket to block + this.setSocketSendBufSize(BYTE_COUNT / 2); + this.sleep = sleep; + } + + @Override + public Writable call(Class protocol, Writable param, long receiveTime) + throws IOException { + if (sleep) { + try { + Thread.sleep(RANDOM.nextInt(20)); // sleep a bit + } catch (InterruptedException e) {} + } + return param; + } + } + + private static class Caller extends Thread { + + private Client client; + private int count; + private InetSocketAddress address; + private boolean failed; + + public Caller(final Client client, final InetSocketAddress address, + final int count) { + this.client = client; + this.address = address; + this.count = count; + } + + @Override + public void run() { + for (int i = 0; i < count; i++) { + try { + int byteSize = RANDOM.nextInt(BYTE_COUNT); + byte[] bytes = new byte[byteSize]; + System.arraycopy(BYTES, 0, bytes, 0, byteSize); + Writable param = new BytesWritable(bytes); + Writable value = client.call(param, address); + Thread.sleep(RANDOM.nextInt(20)); + } catch (Exception e) { + LOG.fatal("Caught: " + e); + failed = true; + } + } + } + } + + public void testServerResponder() throws Exception { + testServerResponder(10, true, 1, 10, 200); + } + + public void testServerResponder(final int handlerCount, + final boolean handlerSleep, + final int clientCount, + final int callerCount, + final int callCount) throws Exception { + Server server = new TestServer(handlerCount, handlerSleep); + server.start(); + + InetSocketAddress address = NetUtils.getConnectAddress(server); + Client[] clients = new Client[clientCount]; + for (int i = 0; i < clientCount; i++) { + clients[i] = new Client(BytesWritable.class, conf); + } + + Caller[] callers = new Caller[callerCount]; + for (int i = 0; i < callerCount; i++) { + callers[i] = new Caller(clients[i % clientCount], address, callCount); + callers[i].start(); + } + for (int i = 0; i < callerCount; i++) { + callers[i].join(); + assertFalse(callers[i].failed); + } + for (int i = 0; i < clientCount; i++) { + clients[i].stop(); + } + server.stop(); + } + +} diff --git a/src/test/org/apache/hadoop/ipc/TestRPC.java b/src/test/org/apache/hadoop/ipc/TestRPC.java new file mode 100644 index 00000000000..d0db263cc1a --- /dev/null +++ b/src/test/org/apache/hadoop/ipc/TestRPC.java @@ -0,0 +1,391 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ipc; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.lang.reflect.Method; + +import junit.framework.TestCase; + +import java.util.Arrays; + +import org.apache.commons.logging.*; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.io.UTF8; +import org.apache.hadoop.io.Writable; + +import org.apache.hadoop.net.NetUtils; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.hadoop.security.authorize.ConfiguredPolicy; +import org.apache.hadoop.security.authorize.PolicyProvider; +import org.apache.hadoop.security.authorize.Service; +import org.apache.hadoop.security.authorize.ServiceAuthorizationManager; + +/** Unit tests for RPC. */ +public class TestRPC extends TestCase { + private static final String ADDRESS = "0.0.0.0"; + + public static final Log LOG = + LogFactory.getLog(TestRPC.class); + + private static Configuration conf = new Configuration(); + + int datasize = 1024*100; + int numThreads = 50; + + public TestRPC(String name) { super(name); } + + public interface TestProtocol extends VersionedProtocol { + public static final long versionID = 1L; + + void ping() throws IOException; + void slowPing(boolean shouldSlow) throws IOException; + String echo(String value) throws IOException; + String[] echo(String[] value) throws IOException; + Writable echo(Writable value) throws IOException; + int add(int v1, int v2) throws IOException; + int add(int[] values) throws IOException; + int error() throws IOException; + void testServerGet() throws IOException; + int[] exchange(int[] values) throws IOException; + } + + public class TestImpl implements TestProtocol { + int fastPingCounter = 0; + + public long getProtocolVersion(String protocol, long clientVersion) { + return TestProtocol.versionID; + } + + public void ping() {} + + public synchronized void slowPing(boolean shouldSlow) { + if (shouldSlow) { + while (fastPingCounter < 2) { + try { + wait(); // slow response until two fast pings happened + } catch (InterruptedException ignored) {} + } + fastPingCounter -= 2; + } else { + fastPingCounter++; + notify(); + } + } + + public String echo(String value) throws IOException { return value; } + + public String[] echo(String[] values) throws IOException { return values; } + + public Writable echo(Writable writable) { + return writable; + } + public int add(int v1, int v2) { + return v1 + v2; + } + + public int add(int[] values) { + int sum = 0; + for (int i = 0; i < values.length; i++) { + sum += values[i]; + } + return sum; + } + + public int error() throws IOException { + throw new IOException("bobo"); + } + + public void testServerGet() throws IOException { + if (!(Server.get() instanceof RPC.Server)) { + throw new IOException("Server.get() failed"); + } + } + + public int[] exchange(int[] values) { + for (int i = 0; i < values.length; i++) { + values[i] = i; + } + return values; + } + } + + // + // an object that does a bunch of transactions + // + static class Transactions implements Runnable { + int datasize; + TestProtocol proxy; + + Transactions(TestProtocol proxy, int datasize) { + this.proxy = proxy; + this.datasize = datasize; + } + + // do two RPC that transfers data. + public void run() { + int[] indata = new int[datasize]; + int[] outdata = null; + int val = 0; + try { + outdata = proxy.exchange(indata); + val = proxy.add(1,2); + } catch (IOException e) { + assertTrue("Exception from RPC exchange() " + e, false); + } + assertEquals(indata.length, outdata.length); + assertEquals(val, 3); + for (int i = 0; i < outdata.length; i++) { + assertEquals(outdata[i], i); + } + } + } + + // + // A class that does an RPC but does not read its response. + // + static class SlowRPC implements Runnable { + private TestProtocol proxy; + private volatile boolean done; + + SlowRPC(TestProtocol proxy) { + this.proxy = proxy; + done = false; + } + + boolean isDone() { + return done; + } + + public void run() { + try { + proxy.slowPing(true); // this would hang until two fast pings happened + done = true; + } catch (IOException e) { + assertTrue("SlowRPC ping exception " + e, false); + } + } + } + + public void testSlowRpc() throws Exception { + System.out.println("Testing Slow RPC"); + // create a server with two handlers + Server server = RPC.getServer(new TestImpl(), ADDRESS, 0, 2, false, conf); + TestProtocol proxy = null; + + try { + server.start(); + + InetSocketAddress addr = NetUtils.getConnectAddress(server); + + // create a client + proxy = (TestProtocol)RPC.getProxy( + TestProtocol.class, TestProtocol.versionID, addr, conf); + + SlowRPC slowrpc = new SlowRPC(proxy); + Thread thread = new Thread(slowrpc, "SlowRPC"); + thread.start(); // send a slow RPC, which won't return until two fast pings + assertTrue("Slow RPC should not have finished1.", !slowrpc.isDone()); + + proxy.slowPing(false); // first fast ping + + // verify that the first RPC is still stuck + assertTrue("Slow RPC should not have finished2.", !slowrpc.isDone()); + + proxy.slowPing(false); // second fast ping + + // Now the slow ping should be able to be executed + while (!slowrpc.isDone()) { + System.out.println("Waiting for slow RPC to get done."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + } + } finally { + server.stop(); + if (proxy != null) { + RPC.stopProxy(proxy); + } + System.out.println("Down slow rpc testing"); + } + } + + + public void testCalls() throws Exception { + Server server = RPC.getServer(new TestImpl(), ADDRESS, 0, conf); + TestProtocol proxy = null; + try { + server.start(); + + InetSocketAddress addr = NetUtils.getConnectAddress(server); + proxy = (TestProtocol)RPC.getProxy( + TestProtocol.class, TestProtocol.versionID, addr, conf); + + proxy.ping(); + + String stringResult = proxy.echo("foo"); + assertEquals(stringResult, "foo"); + + stringResult = proxy.echo((String)null); + assertEquals(stringResult, null); + + String[] stringResults = proxy.echo(new String[]{"foo","bar"}); + assertTrue(Arrays.equals(stringResults, new String[]{"foo","bar"})); + + stringResults = proxy.echo((String[])null); + assertTrue(Arrays.equals(stringResults, null)); + + UTF8 utf8Result = (UTF8)proxy.echo(new UTF8("hello world")); + assertEquals(utf8Result, new UTF8("hello world")); + + utf8Result = (UTF8)proxy.echo((UTF8)null); + assertEquals(utf8Result, null); + + int intResult = proxy.add(1, 2); + assertEquals(intResult, 3); + + intResult = proxy.add(new int[] {1, 2}); + assertEquals(intResult, 3); + + boolean caught = false; + try { + proxy.error(); + } catch (IOException e) { + LOG.debug("Caught " + e); + caught = true; + } + assertTrue(caught); + + proxy.testServerGet(); + + // create multiple threads and make them do large data transfers + System.out.println("Starting multi-threaded RPC test..."); + server.setSocketSendBufSize(1024); + Thread threadId[] = new Thread[numThreads]; + for (int i = 0; i < numThreads; i++) { + Transactions trans = new Transactions(proxy, datasize); + threadId[i] = new Thread(trans, "TransactionThread-" + i); + threadId[i].start(); + } + + // wait for all transactions to get over + System.out.println("Waiting for all threads to finish RPCs..."); + for (int i = 0; i < numThreads; i++) { + try { + threadId[i].join(); + } catch (InterruptedException e) { + i--; // retry + } + } + + // try some multi-calls + Method echo = + TestProtocol.class.getMethod("echo", new Class[] { String.class }); + String[] strings = (String[])RPC.call(echo, new String[][]{{"a"},{"b"}}, + new InetSocketAddress[] {addr, addr}, conf); + assertTrue(Arrays.equals(strings, new String[]{"a","b"})); + + Method ping = TestProtocol.class.getMethod("ping", new Class[] {}); + Object[] voids = (Object[])RPC.call(ping, new Object[][]{{},{}}, + new InetSocketAddress[] {addr, addr}, conf); + assertEquals(voids, null); + } finally { + server.stop(); + if(proxy!=null) RPC.stopProxy(proxy); + } + } + + public void testStandaloneClient() throws IOException { + try { + RPC.waitForProxy(TestProtocol.class, + TestProtocol.versionID, new InetSocketAddress(ADDRESS, 20), conf, 15000L); + fail("We should not have reached here"); + } catch (ConnectException ioe) { + //this is what we expected + } + } + + private static final String ACL_CONFIG = "test.protocol.acl"; + + private static class TestPolicyProvider extends PolicyProvider { + + @Override + public Service[] getServices() { + return new Service[] { new Service(ACL_CONFIG, TestProtocol.class) }; + } + + } + + private void doRPCs(Configuration conf, boolean expectFailure) throws Exception { + SecurityUtil.setPolicy(new ConfiguredPolicy(conf, new TestPolicyProvider())); + + Server server = RPC.getServer(new TestImpl(), ADDRESS, 0, 5, true, conf); + + TestProtocol proxy = null; + + server.start(); + + InetSocketAddress addr = NetUtils.getConnectAddress(server); + + try { + proxy = (TestProtocol)RPC.getProxy( + TestProtocol.class, TestProtocol.versionID, addr, conf); + proxy.ping(); + + if (expectFailure) { + fail("Expect RPC.getProxy to fail with AuthorizationException!"); + } + } catch (RemoteException e) { + if (expectFailure) { + assertTrue(e.unwrapRemoteException() instanceof AuthorizationException); + } else { + throw e; + } + } finally { + server.stop(); + if (proxy != null) { + RPC.stopProxy(proxy); + } + } + } + + public void testAuthorization() throws Exception { + Configuration conf = new Configuration(); + conf.setBoolean( + ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, true); + + // Expect to succeed + conf.set(ACL_CONFIG, "*"); + doRPCs(conf, false); + + // Reset authorization to expect failure + conf.set(ACL_CONFIG, "invalid invalid"); + doRPCs(conf, true); + } + + public static void main(String[] args) throws Exception { + + new TestRPC("test").testCalls(); + + } +} diff --git a/src/test/org/apache/hadoop/log/TestLogLevel.java b/src/test/org/apache/hadoop/log/TestLogLevel.java new file mode 100644 index 00000000000..f2443c04d90 --- /dev/null +++ b/src/test/org/apache/hadoop/log/TestLogLevel.java @@ -0,0 +1,78 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.hadoop.log; + +import java.io.*; +import java.net.*; + +import org.apache.hadoop.http.HttpServer; + +import junit.framework.TestCase; +import org.apache.commons.logging.*; +import org.apache.commons.logging.impl.*; +import org.apache.log4j.*; + +public class TestLogLevel extends TestCase { + static final PrintStream out = System.out; + + public void testDynamicLogLevel() throws Exception { + String logName = TestLogLevel.class.getName(); + Log testlog = LogFactory.getLog(logName); + + //only test Log4JLogger + if (testlog instanceof Log4JLogger) { + Logger log = ((Log4JLogger)testlog).getLogger(); + log.debug("log.debug1"); + log.info("log.info1"); + log.error("log.error1"); + assertTrue(!Level.ERROR.equals(log.getEffectiveLevel())); + + HttpServer server = new HttpServer("..", "localhost", 22222, true); + server.start(); + int port = server.getPort(); + + //servlet + URL url = new URL("http://localhost:" + port + + "/logLevel?log=" + logName + "&level=" + Level.ERROR); + out.println("*** Connecting to " + url); + URLConnection connection = url.openConnection(); + connection.connect(); + + BufferedReader in = new BufferedReader(new InputStreamReader( + connection.getInputStream())); + for(String line; (line = in.readLine()) != null; out.println(line)); + in.close(); + + log.debug("log.debug2"); + log.info("log.info2"); + log.error("log.error2"); + assertTrue(Level.ERROR.equals(log.getEffectiveLevel())); + + //command line + String[] args = {"-setlevel", "localhost:"+port, logName,""+Level.DEBUG}; + LogLevel.main(args); + log.debug("log.debug3"); + log.info("log.info3"); + log.error("log.error3"); + assertTrue(Level.DEBUG.equals(log.getEffectiveLevel())); + } + else { + out.println(testlog.getClass() + " not tested."); + } + } +} diff --git a/src/test/org/apache/hadoop/metrics/TestMetricsServlet.java b/src/test/org/apache/hadoop/metrics/TestMetricsServlet.java new file mode 100644 index 00000000000..8d5cfc9a553 --- /dev/null +++ b/src/test/org/apache/hadoop/metrics/TestMetricsServlet.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.metrics; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.hadoop.metrics.MetricsServlet.TagsMetricsPair; +import org.apache.hadoop.metrics.spi.NoEmitMetricsContext; +import org.apache.hadoop.metrics.spi.OutputRecord; +import org.mortbay.util.ajax.JSON; + +public class TestMetricsServlet extends TestCase { + MetricsContext nc1; + MetricsContext nc2; + // List containing nc1 and nc2. + List contexts; + OutputRecord outputRecord; + + /** + * Initializes, for testing, two NoEmitMetricsContext's, and adds one value + * to the first of them. + */ + public void setUp() throws IOException { + nc1 = new NoEmitMetricsContext(); + nc1.init("test1", ContextFactory.getFactory()); + nc2 = new NoEmitMetricsContext(); + nc2.init("test2", ContextFactory.getFactory()); + contexts = new ArrayList(); + contexts.add(nc1); + contexts.add(nc2); + + MetricsRecord r = nc1.createRecord("testRecord"); + + r.setTag("testTag1", "testTagValue1"); + r.setTag("testTag2", "testTagValue2"); + r.setMetric("testMetric1", 1); + r.setMetric("testMetric2", 33); + r.update(); + + Map> m = nc1.getAllRecords(); + assertEquals(1, m.size()); + assertEquals(1, m.values().size()); + Collection outputRecords = m.values().iterator().next(); + assertEquals(1, outputRecords.size()); + outputRecord = outputRecords.iterator().next(); + } + + + + public void testTagsMetricsPair() throws IOException { + TagsMetricsPair pair = new TagsMetricsPair(outputRecord.getTagsCopy(), + outputRecord.getMetricsCopy()); + String s = JSON.toString(pair); + assertEquals( + "[{\"testTag1\":\"testTagValue1\",\"testTag2\":\"testTagValue2\"},"+ + "{\"testMetric1\":1,\"testMetric2\":33}]", s); + } + + public void testGetMap() throws IOException { + MetricsServlet servlet = new MetricsServlet(); + Map>> m = servlet.makeMap(contexts); + assertEquals("Map missing contexts", 2, m.size()); + assertTrue(m.containsKey("test1")); + + Map> m2 = m.get("test1"); + + assertEquals("Missing records", 1, m2.size()); + assertTrue(m2.containsKey("testRecord")); + assertEquals("Wrong number of tags-values pairs.", 1, m2.get("testRecord").size()); + } + + public void testPrintMap() throws IOException { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + MetricsServlet servlet = new MetricsServlet(); + servlet.printMap(out, servlet.makeMap(contexts)); + + String EXPECTED = "" + + "test1\n" + + " testRecord\n" + + " {testTag1=testTagValue1,testTag2=testTagValue2}:\n" + + " testMetric1=1\n" + + " testMetric2=33\n" + + "test2\n"; + assertEquals(EXPECTED, sw.toString()); + } +} diff --git a/src/test/org/apache/hadoop/metrics/spi/TestOutputRecord.java b/src/test/org/apache/hadoop/metrics/spi/TestOutputRecord.java new file mode 100644 index 00000000000..02e94a9f1b0 --- /dev/null +++ b/src/test/org/apache/hadoop/metrics/spi/TestOutputRecord.java @@ -0,0 +1,38 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.metrics.spi; + +import org.apache.hadoop.metrics.spi.AbstractMetricsContext.MetricMap; +import org.apache.hadoop.metrics.spi.AbstractMetricsContext.TagMap; + +import junit.framework.TestCase; + +public class TestOutputRecord extends TestCase { + public void testCopy() { + TagMap tags = new TagMap(); + tags.put("tagkey", "tagval"); + MetricMap metrics = new MetricMap(); + metrics.put("metrickey", 123.4); + OutputRecord r = new OutputRecord(tags, metrics); + + assertEquals(tags, r.getTagsCopy()); + assertNotSame(tags, r.getTagsCopy()); + assertEquals(metrics, r.getMetricsCopy()); + assertNotSame(metrics, r.getMetricsCopy()); + } +} diff --git a/src/test/org/apache/hadoop/net/StaticMapping.java b/src/test/org/apache/hadoop/net/StaticMapping.java new file mode 100644 index 00000000000..c3923ed9510 --- /dev/null +++ b/src/test/org/apache/hadoop/net/StaticMapping.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.net; + +import java.util.*; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; + +/** + * Implements the {@link DNSToSwitchMapping} via static mappings. Used + * in testcases that simulate racks. + * + */ +public class StaticMapping extends Configured implements DNSToSwitchMapping { + public void setconf(Configuration conf) { + String[] mappings = conf.getStrings("hadoop.configured.node.mapping"); + if (mappings != null) { + for (int i = 0; i < mappings.length; i++) { + String str = mappings[i]; + String host = str.substring(0, str.indexOf('=')); + String rack = str.substring(str.indexOf('=') + 1); + addNodeToRack(host, rack); + } + } + } + /* Only one instance per JVM */ + private static Map nameToRackMap = new HashMap(); + + static synchronized public void addNodeToRack(String name, String rackId) { + nameToRackMap.put(name, rackId); + } + public List resolve(List names) { + List m = new ArrayList(); + synchronized (nameToRackMap) { + for (String name : names) { + String rackId; + if ((rackId = nameToRackMap.get(name)) != null) { + m.add(rackId); + } else { + m.add(NetworkTopology.DEFAULT_RACK); + } + } + return m; + } + } +} diff --git a/src/test/org/apache/hadoop/net/TestDNS.java b/src/test/org/apache/hadoop/net/TestDNS.java new file mode 100644 index 00000000000..5825ecf8c63 --- /dev/null +++ b/src/test/org/apache/hadoop/net/TestDNS.java @@ -0,0 +1,150 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.hadoop.net; + +import junit.framework.TestCase; + +import java.net.UnknownHostException; +import java.net.InetAddress; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.naming.NameNotFoundException; + +/** + * + */ +public class TestDNS extends TestCase { + + private static final Log LOG = LogFactory.getLog(TestDNS.class); + private static final String DEFAULT = "default"; + + /** + * Constructs a test case with the given name. + * + * @param name test name + */ + public TestDNS(String name) { + super(name); + } + + /** + * Test that asking for the default hostname works + * @throws Exception if hostname lookups fail */ + public void testGetLocalHost() throws Exception { + String hostname = DNS.getDefaultHost(DEFAULT); + assertNotNull(hostname); + } + + /** + * Test that repeated calls to getting the local host are fairly fast, and + * hence that caching is being used + * @throws Exception if hostname lookups fail + */ + public void testGetLocalHostIsFast() throws Exception { + String hostname = DNS.getDefaultHost(DEFAULT); + assertNotNull(hostname); + long t1 = System.currentTimeMillis(); + String hostname2 = DNS.getDefaultHost(DEFAULT); + long t2 = System.currentTimeMillis(); + String hostname3 = DNS.getDefaultHost(DEFAULT); + long t3 = System.currentTimeMillis(); + assertEquals(hostname3, hostname2); + assertEquals(hostname2, hostname); + long interval2 = t3 - t2; + assertTrue( + "It is taking to long to determine the local host -caching is not working", + interval2 < 20000); + } + + /** + * Test that our local IP address is not null + * @throws Exception if something went wrong + */ + public void testLocalHostHasAnAddress() throws Exception { + assertNotNull(getLocalIPAddr()); + } + + private InetAddress getLocalIPAddr() throws UnknownHostException { + String hostname = DNS.getDefaultHost(DEFAULT); + InetAddress localhost = InetAddress.getByName(hostname); + return localhost; + } + + /** + * Test that passing a null pointer is as the interface + * fails with a NullPointerException + * @throws Exception if something went wrong + */ + public void testNullInterface() throws Exception { + try { + String host = DNS.getDefaultHost(null); + fail("Expected a NullPointerException, got " + host); + } catch (NullPointerException expected) { + //this is expected + } + } + + /** + * Get the IP addresses of an unknown interface, expect to get something + * back + * @throws Exception if something went wrong + */ + public void testIPsOfUnknownInterface() throws Exception { + String[] ips = DNS.getIPs("name-of-an-unknown-interface"); + assertNotNull(ips); + assertTrue(ips.length > 0); + } + + /** + * TestCase: get our local address and reverse look it up + * @throws Exception if that fails + */ + public void testRDNS() throws Exception { + InetAddress localhost = getLocalIPAddr(); + try { + String s = DNS.reverseDns(localhost, null); + LOG.info("Local revers DNS hostname is " + s); + } catch (NameNotFoundException e) { + if (!localhost.isLinkLocalAddress() || localhost.isLoopbackAddress()) { + //these addresses probably won't work with rDNS anyway, unless someone + //has unusual entries in their DNS server mapping 1.0.0.127 to localhost + LOG.info("Reverse DNS failing as due to incomplete networking", e); + LOG.info("Address is " + localhost + + " Loopback=" + localhost.isLoopbackAddress() + + " Linklocal=" + localhost.isLinkLocalAddress()); + } + + } + } + + /** + * Test that the name "localhost" resolves to something. + * + * If this fails, your machine's network is in a mess, go edit /etc/hosts + * @throws Exception for any problems + */ + public void testLocalhostResolves() throws Exception { + InetAddress localhost = InetAddress.getByName("localhost"); + assertNotNull("localhost is null", localhost); + LOG.info("Localhost IPAddr is " + localhost.toString()); + } +} diff --git a/src/test/org/apache/hadoop/net/TestScriptBasedMapping.java b/src/test/org/apache/hadoop/net/TestScriptBasedMapping.java new file mode 100644 index 00000000000..144dbaa0e36 --- /dev/null +++ b/src/test/org/apache/hadoop/net/TestScriptBasedMapping.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.net; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.hadoop.conf.Configuration; + +import junit.framework.TestCase; + +public class TestScriptBasedMapping extends TestCase { + + public void testNoArgsMeansNoResult() { + ScriptBasedMapping mapping = new ScriptBasedMapping(); + + Configuration conf = new Configuration(); + conf.setInt(ScriptBasedMapping.SCRIPT_ARG_COUNT_KEY, + ScriptBasedMapping.MIN_ALLOWABLE_ARGS - 1); + conf.set(ScriptBasedMapping.SCRIPT_FILENAME_KEY, "any-filename"); + + mapping.setConf(conf); + + List names = new ArrayList(); + names.add("some.machine.name"); + names.add("other.machine.name"); + + List result = mapping.resolve(names); + assertNull(result); + } +} diff --git a/src/test/org/apache/hadoop/net/TestSocketIOWithTimeout.java b/src/test/org/apache/hadoop/net/TestSocketIOWithTimeout.java new file mode 100644 index 00000000000..53f320917c5 --- /dev/null +++ b/src/test/org/apache/hadoop/net/TestSocketIOWithTimeout.java @@ -0,0 +1,155 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketTimeoutException; +import java.nio.channels.Pipe; +import java.util.Arrays; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import junit.framework.TestCase; + +/** + * This tests timout out from SocketInputStream and + * SocketOutputStream using pipes. + * + * Normal read and write using these streams are tested by pretty much + * every DFS unit test. + */ +public class TestSocketIOWithTimeout extends TestCase { + + static Log LOG = LogFactory.getLog(TestSocketIOWithTimeout.class); + + private static int TIMEOUT = 1*1000; + private static String TEST_STRING = "1234567890"; + + private void doIO(InputStream in, OutputStream out) throws IOException { + /* Keep on writing or reading until we get SocketTimeoutException. + * It expects this exception to occur within 100 millis of TIMEOUT. + */ + byte buf[] = new byte[4192]; + + while (true) { + long start = System.currentTimeMillis(); + try { + if (in != null) { + in.read(buf); + } else { + out.write(buf); + } + } catch (SocketTimeoutException e) { + long diff = System.currentTimeMillis() - start; + LOG.info("Got SocketTimeoutException as expected after " + + diff + " millis : " + e.getMessage()); + assertTrue(Math.abs(TIMEOUT - diff) <= 200); + break; + } + } + } + + /** + * Just reads one byte from the input stream. + */ + static class ReadRunnable implements Runnable { + private InputStream in; + + public ReadRunnable(InputStream in) { + this.in = in; + } + public void run() { + try { + in.read(); + } catch (IOException e) { + LOG.info("Got expection while reading as expected : " + + e.getMessage()); + return; + } + assertTrue(false); + } + } + + public void testSocketIOWithTimeout() throws IOException { + + // first open pipe: + Pipe pipe = Pipe.open(); + Pipe.SourceChannel source = pipe.source(); + Pipe.SinkChannel sink = pipe.sink(); + + try { + InputStream in = new SocketInputStream(source, TIMEOUT); + OutputStream out = new SocketOutputStream(sink, TIMEOUT); + + byte[] writeBytes = TEST_STRING.getBytes(); + byte[] readBytes = new byte[writeBytes.length]; + + out.write(writeBytes); + doIO(null, out); + + in.read(readBytes); + assertTrue(Arrays.equals(writeBytes, readBytes)); + doIO(in, null); + + /* + * Verify that it handles interrupted threads properly. + * Use a large timeout and expect the thread to return quickly. + */ + in = new SocketInputStream(source, 0); + Thread thread = new Thread(new ReadRunnable(in)); + thread.start(); + + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) {} + + thread.interrupt(); + + try { + thread.join(); + } catch (InterruptedException e) { + throw new IOException("Unexpected InterruptedException : " + e); + } + + //make sure the channels are still open + assertTrue(source.isOpen()); + assertTrue(sink.isOpen()); + + out.close(); + assertFalse(sink.isOpen()); + + // close sink and expect -1 from source.read() + assertEquals(-1, in.read()); + + // make sure close() closes the underlying channel. + in.close(); + assertFalse(source.isOpen()); + + } finally { + if (source != null) { + source.close(); + } + if (sink != null) { + sink.close(); + } + } + } +} diff --git a/src/test/org/apache/hadoop/record/FromCpp.java b/src/test/org/apache/hadoop/record/FromCpp.java new file mode 100644 index 00000000000..2cd2271f43b --- /dev/null +++ b/src/test/org/apache/hadoop/record/FromCpp.java @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.record; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.TreeMap; +import junit.framework.*; + +/** + */ +public class FromCpp extends TestCase { + + public FromCpp(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + } + + public void testBinary() { + File tmpfile; + try { + tmpfile = new File("/temp/hadooptmp.dat"); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + FileInputStream istream = new FileInputStream(tmpfile); + BinaryRecordInput in = new BinaryRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + assertTrue(r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testCsv() { + File tmpfile; + try { + tmpfile = new File("/temp/hadooptmp.txt"); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + FileInputStream istream = new FileInputStream(tmpfile); + CsvRecordInput in = new CsvRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + assertTrue(r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testXml() { + File tmpfile; + try { + tmpfile = new File("/temp/hadooptmp.xml"); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + FileInputStream istream = new FileInputStream(tmpfile); + XmlRecordInput in = new XmlRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + assertTrue(r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + +} diff --git a/src/test/org/apache/hadoop/record/RecordBench.java b/src/test/org/apache/hadoop/record/RecordBench.java new file mode 100644 index 00000000000..1cba75ed804 --- /dev/null +++ b/src/test/org/apache/hadoop/record/RecordBench.java @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.record; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Random; + +/** + * Benchmark for various types of serializations + */ +public class RecordBench { + + private static class Times { + long init; + long serialize; + long deserialize; + long write; + long readFields; + }; + + private static final long SEED = 0xDEADBEEFL; + private static final Random rand = new Random(); + + /** Do not allow to create a new instance of RecordBench */ + private RecordBench() {} + + private static void initBuffers(Record[] buffers) { + final int BUFLEN = 32; + for (int idx = 0; idx < buffers.length; idx++) { + buffers[idx] = new RecBuffer(); + int buflen = rand.nextInt(BUFLEN); + byte[] bytes = new byte[buflen]; + rand.nextBytes(bytes); + ((RecBuffer)buffers[idx]).setData(new Buffer(bytes)); + } + } + + private static void initStrings(Record[] strings) { + final int STRLEN = 32; + for (int idx = 0; idx < strings.length; idx++) { + strings[idx] = new RecString(); + int strlen = rand.nextInt(STRLEN); + StringBuilder sb = new StringBuilder(strlen); + for (int ich = 0; ich < strlen; ich++) { + int cpt = 0; + while (true) { + cpt = rand.nextInt(0x10FFFF+1); + if (Utils.isValidCodePoint(cpt)) { + break; + } + } + sb.appendCodePoint(cpt); + } + ((RecString)strings[idx]).setData(sb.toString()); + } + } + + private static void initInts(Record[] ints) { + for (int idx = 0; idx < ints.length; idx++) { + ints[idx] = new RecInt(); + ((RecInt)ints[idx]).setData(rand.nextInt()); + } + } + + private static Record[] makeArray(String type, int numRecords, Times times) { + Method init = null; + try { + init = RecordBench.class.getDeclaredMethod("init"+ + toCamelCase(type) + "s", + new Class[] {Record[].class}); + } catch (NoSuchMethodException ex) { + throw new RuntimeException(ex); + } + + Record[] records = new Record[numRecords]; + times.init = System.nanoTime(); + try { + init.invoke(null, new Object[]{records}); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + times.init = System.nanoTime() - times.init; + return records; + } + + private static void runBinaryBench(String type, int numRecords, Times times) + throws IOException { + Record[] records = makeArray(type, numRecords, times); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + BinaryRecordOutput rout = new BinaryRecordOutput(bout); + DataOutputStream dout = new DataOutputStream(bout); + + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + bout.reset(); + + times.serialize = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + times.serialize = System.nanoTime() - times.serialize; + + byte[] serialized = bout.toByteArray(); + ByteArrayInputStream bin = new ByteArrayInputStream(serialized); + BinaryRecordInput rin = new BinaryRecordInput(bin); + + times.deserialize = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].deserialize(rin); + } + times.deserialize = System.nanoTime() - times.deserialize; + + bout.reset(); + + times.write = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].write(dout); + } + times.write = System.nanoTime() - times.write; + + bin.reset(); + DataInputStream din = new DataInputStream(bin); + + times.readFields = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].readFields(din); + } + times.readFields = System.nanoTime() - times.readFields; + } + + private static void runCsvBench(String type, int numRecords, Times times) + throws IOException { + Record[] records = makeArray(type, numRecords, times); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + CsvRecordOutput rout = new CsvRecordOutput(bout); + + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + bout.reset(); + + times.serialize = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + times.serialize = System.nanoTime() - times.serialize; + + byte[] serialized = bout.toByteArray(); + ByteArrayInputStream bin = new ByteArrayInputStream(serialized); + CsvRecordInput rin = new CsvRecordInput(bin); + + times.deserialize = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].deserialize(rin); + } + times.deserialize = System.nanoTime() - times.deserialize; + } + + private static void runXmlBench(String type, int numRecords, Times times) + throws IOException { + Record[] records = makeArray(type, numRecords, times); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + XmlRecordOutput rout = new XmlRecordOutput(bout); + + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + bout.reset(); + + bout.write("\n".getBytes()); + + times.serialize = System.nanoTime(); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].serialize(rout); + } + times.serialize = System.nanoTime() - times.serialize; + + bout.write("\n".getBytes()); + + byte[] serialized = bout.toByteArray(); + ByteArrayInputStream bin = new ByteArrayInputStream(serialized); + + times.deserialize = System.nanoTime(); + XmlRecordInput rin = new XmlRecordInput(bin); + for(int idx = 0; idx < numRecords; idx++) { + records[idx].deserialize(rin); + } + times.deserialize = System.nanoTime() - times.deserialize; + } + + private static void printTimes(String type, + String format, + int numRecords, + Times times) { + System.out.println("Type: " + type + " Format: " + format + + " #Records: "+numRecords); + if (times.init != 0) { + System.out.println("Initialization Time (Per record) : "+ + times.init/numRecords + " Nanoseconds"); + } + + if (times.serialize != 0) { + System.out.println("Serialization Time (Per Record) : "+ + times.serialize/numRecords + " Nanoseconds"); + } + + if (times.deserialize != 0) { + System.out.println("Deserialization Time (Per Record) : "+ + times.deserialize/numRecords + " Nanoseconds"); + } + + if (times.write != 0) { + System.out.println("Write Time (Per Record) : "+ + times.write/numRecords + " Nanoseconds"); + } + + if (times.readFields != 0) { + System.out.println("ReadFields Time (Per Record) : "+ + times.readFields/numRecords + " Nanoseconds"); + } + + System.out.println(); + } + + private static String toCamelCase(String inp) { + char firstChar = inp.charAt(0); + if (Character.isLowerCase(firstChar)) { + return ""+Character.toUpperCase(firstChar) + inp.substring(1); + } + return inp; + } + + private static void exitOnError() { + String usage = "RecordBench {buffer|string|int}"+ + " {binary|csv|xml} "; + System.out.println(usage); + System.exit(1); + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws IOException { + String version = "RecordBench v0.1"; + System.out.println(version+"\n"); + + if (args.length != 3) { + exitOnError(); + } + + String typeName = args[0]; + String format = args[1]; + int numRecords = Integer.decode(args[2]).intValue(); + + Method bench = null; + try { + bench = RecordBench.class.getDeclaredMethod("run"+ + toCamelCase(format) + "Bench", + new Class[] {String.class, Integer.TYPE, Times.class}); + } catch (NoSuchMethodException ex) { + ex.printStackTrace(); + exitOnError(); + } + + if (numRecords < 0) { + exitOnError(); + } + + // dry run + rand.setSeed(SEED); + Times times = new Times(); + try { + bench.invoke(null, new Object[] {typeName, numRecords, times}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(1); + } + + // timed run + rand.setSeed(SEED); + try { + bench.invoke(null, new Object[] {typeName, numRecords, times}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(1); + } + printTimes(typeName, format, numRecords, times); + } +} diff --git a/src/test/org/apache/hadoop/record/TestBuffer.java b/src/test/org/apache/hadoop/record/TestBuffer.java new file mode 100644 index 00000000000..3012fa6ff46 --- /dev/null +++ b/src/test/org/apache/hadoop/record/TestBuffer.java @@ -0,0 +1,124 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.record; + +import junit.framework.*; + +/** + * A Unit test for Record I/O Buffer class + */ +public class TestBuffer extends TestCase { + + public TestBuffer(String testName) { + super(testName); + } + + /** + * Test of set method, of class org.apache.hadoop.record.Buffer. + */ + public void testSet() { + final byte[] bytes = new byte[10]; + final Buffer instance = new Buffer(); + + instance.set(bytes); + + assertEquals("set failed", bytes, instance.get()); + } + + /** + * Test of copy method, of class org.apache.hadoop.record.Buffer. + */ + public void testCopy() { + final byte[] bytes = new byte[10]; + final int offset = 6; + final int length = 3; + for (int idx = 0; idx < 10; idx ++) { + bytes[idx] = (byte) idx; + } + final Buffer instance = new Buffer(); + + instance.copy(bytes, offset, length); + + assertEquals("copy failed", 3, instance.getCapacity()); + assertEquals("copy failed", 3, instance.get().length); + for (int idx = 0; idx < 3; idx++) { + assertEquals("Buffer content corrupted", idx+6, instance.get()[idx]); + } + } + + /** + * Test of getCount method, of class org.apache.hadoop.record.Buffer. + */ + public void testGetCount() { + final Buffer instance = new Buffer(); + + final int expResult = 0; + final int result = instance.getCount(); + assertEquals("getSize failed", expResult, result); + } + + /** + * Test of getCapacity method, of class org.apache.hadoop.record.Buffer. + */ + public void testGetCapacity() { + final Buffer instance = new Buffer(); + + final int expResult = 0; + final int result = instance.getCapacity(); + assertEquals("getCapacity failed", expResult, result); + + instance.setCapacity(100); + assertEquals("setCapacity failed", 100, instance.getCapacity()); + } + + /** + * Test of truncate method, of class org.apache.hadoop.record.Buffer. + */ + public void testTruncate() { + final Buffer instance = new Buffer(); + instance.setCapacity(100); + assertEquals("setCapacity failed", 100, instance.getCapacity()); + + instance.truncate(); + assertEquals("truncate failed", 0, instance.getCapacity()); + } + + /** + * Test of append method, of class org.apache.hadoop.record.Buffer. + */ + public void testAppend() { + final byte[] bytes = new byte[100]; + final int offset = 0; + final int length = 100; + for (int idx = 0; idx < 100; idx++) { + bytes[idx] = (byte) (100-idx); + } + + final Buffer instance = new Buffer(); + + instance.append(bytes, offset, length); + + assertEquals("Buffer size mismatch", 100, instance.getCount()); + + for (int idx = 0; idx < 100; idx++) { + assertEquals("Buffer contents corrupted", 100-idx, instance.get()[idx]); + } + + } +} diff --git a/src/test/org/apache/hadoop/record/TestRecordIO.java b/src/test/org/apache/hadoop/record/TestRecordIO.java new file mode 100644 index 00000000000..163ec1b00b2 --- /dev/null +++ b/src/test/org/apache/hadoop/record/TestRecordIO.java @@ -0,0 +1,199 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.record; + +import java.io.IOException; +import junit.framework.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.TreeMap; + +/** + */ +public class TestRecordIO extends TestCase { + + public TestRecordIO(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + } + + public void testBinary() { + File tmpfile; + try { + tmpfile = File.createTempFile("hadooprec", ".dat"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + BinaryRecordOutput out = new BinaryRecordOutput(ostream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(-4567); + r1.setLongVal(-2367L); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other random text"); + r1.setRecordVal(r0); + r1.serialize(out, ""); + ostream.close(); + FileInputStream istream = new FileInputStream(tmpfile); + BinaryRecordInput in = new BinaryRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + tmpfile.delete(); + assertTrue("Serialized and deserialized records do not match.", r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testCsv() { + File tmpfile; + try { + tmpfile = File.createTempFile("hadooprec", ".txt"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + CsvRecordOutput out = new CsvRecordOutput(ostream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other random text"); + r1.setRecordVal(r0); + r1.serialize(out, ""); + ostream.close(); + FileInputStream istream = new FileInputStream(tmpfile); + CsvRecordInput in = new CsvRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + tmpfile.delete(); + assertTrue("Serialized and deserialized records do not match.", r1.equals(r2)); + + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testToString() { + try { + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + byte[] barr = new byte[256]; + for (int idx = 0; idx < 256; idx++) { + barr[idx] = (byte) idx; + } + r1.setBufferVal(new Buffer(barr)); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other random text"); + r1.setRecordVal(r0); + System.err.println("Illustrating toString bug"+r1.toString()); + System.err.println("Illustrating toString bug"+r1.toString()); + } catch (Throwable ex) { + assertTrue("Record.toString cannot be invoked twice in succession."+ + "This bug has been fixed in the latest version.", false); + } + } + + public void testXml() { + File tmpfile; + try { + tmpfile = File.createTempFile("hadooprec", ".xml"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + XmlRecordOutput out = new XmlRecordOutput(ostream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("ran\002dom < %text<&more\uffff"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other %rando\007m & >&more text"); + r1.setRecordVal(r0); + r1.serialize(out, ""); + ostream.close(); + FileInputStream istream = new FileInputStream(tmpfile); + XmlRecordInput in = new XmlRecordInput(istream); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + tmpfile.delete(); + assertTrue("Serialized and deserialized records do not match.", r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testCloneable() { + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(-4567); + r1.setLongVal(-2367L); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other random text"); + r1.setRecordVal(r0); + try { + RecRecord1 r2 = (RecRecord1) r1.clone(); + assertTrue("Cloneable semantics violated. r1==r2", r1 != r2); + assertTrue("Cloneable semantics violated. r1.getClass() != r2.getClass()", + r1.getClass() == r2.getClass()); + assertTrue("Cloneable semantics violated. !r2.equals(r1)", r2.equals(r1)); + } catch (final CloneNotSupportedException ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/test/org/apache/hadoop/record/TestRecordVersioning.java b/src/test/org/apache/hadoop/record/TestRecordVersioning.java new file mode 100644 index 00000000000..129ba2ced86 --- /dev/null +++ b/src/test/org/apache/hadoop/record/TestRecordVersioning.java @@ -0,0 +1,239 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.record; + +import java.io.IOException; +import junit.framework.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.TreeMap; +import org.apache.hadoop.record.meta.RecordTypeInfo; + +/** + */ +public class TestRecordVersioning extends TestCase { + + public TestRecordVersioning(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + } + + /* + * basic versioning + * write out a record and its type info, read it back using its typeinfo + */ + public void testBasic() { + File tmpfile, tmpRTIfile; + try { + tmpfile = File.createTempFile("hadooprec", ".dat"); + tmpRTIfile = File.createTempFile("hadooprti", ".dat"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + BinaryRecordOutput out = new BinaryRecordOutput(ostream); + FileOutputStream oRTIstream = new FileOutputStream(tmpRTIfile); + BinaryRecordOutput outRTI = new BinaryRecordOutput(oRTIstream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(-4567); + r1.setLongVal(-2367L); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + RecRecord0 r0 = new RecRecord0(); + r0.setStringVal("other random text"); + r1.setRecordVal(r0); + r1.serialize(out, ""); + ostream.close(); + // write out the type info + RecRecord1.getTypeInfo().serialize(outRTI); + oRTIstream.close(); + + // read + FileInputStream istream = new FileInputStream(tmpfile); + BinaryRecordInput in = new BinaryRecordInput(istream); + FileInputStream iRTIstream = new FileInputStream(tmpRTIfile); + BinaryRecordInput inRTI = new BinaryRecordInput(iRTIstream); + RecordTypeInfo rti = new RecordTypeInfo(); + rti.deserialize(inRTI); + iRTIstream.close(); + RecRecord1.setTypeFilter(rti); + RecRecord1 r2 = new RecRecord1(); + r2.deserialize(in, ""); + istream.close(); + tmpfile.delete(); + tmpRTIfile.delete(); + assertTrue("Serialized and deserialized versioned records do not match.", r1.equals(r2)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /* + * versioning + * write out a record and its type info, read back a similar record using the written record's typeinfo + */ + public void testVersioning() { + File tmpfile, tmpRTIfile; + try { + tmpfile = File.createTempFile("hadooprec", ".dat"); + tmpRTIfile = File.createTempFile("hadooprti", ".dat"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + BinaryRecordOutput out = new BinaryRecordOutput(ostream); + FileOutputStream oRTIstream = new FileOutputStream(tmpRTIfile); + BinaryRecordOutput outRTI = new BinaryRecordOutput(oRTIstream); + + // we create an array of records to write + ArrayList recsWrite = new ArrayList(); + int i, j, k, l; + for (i=0; i<5; i++) { + RecRecordOld s1Rec = new RecRecordOld(); + + s1Rec.setName("This is record s1: " + i); + + ArrayList iA = new ArrayList(); + for (j=0; j<3; j++) { + iA.add(new Long(i+j)); + } + s1Rec.setIvec(iA); + + ArrayList> ssVec = new ArrayList>(); + for (j=0; j<2; j++) { + ArrayList sVec = new ArrayList(); + for (k=0; k<3; k++) { + RecRecord0 sRec = new RecRecord0("This is record s: ("+j+": "+k+")"); + sVec.add(sRec); + } + ssVec.add(sVec); + } + s1Rec.setSvec(ssVec); + + s1Rec.setInner(new RecRecord0("This is record s: " + i)); + + ArrayList>> aaaVec = new ArrayList>>(); + for (l=0; l<2; l++) { + ArrayList> aaVec = new ArrayList>(); + for (j=0; j<2; j++) { + ArrayList aVec = new ArrayList(); + for (k=0; k<3; k++) { + aVec.add(new String("THis is a nested string: (" + l + ": " + j + ": " + k + ")")); + } + aaVec.add(aVec); + } + aaaVec.add(aaVec); + } + s1Rec.setStrvec(aaaVec); + + s1Rec.setI1(100+i); + + java.util.TreeMap map1 = new java.util.TreeMap(); + map1.put(new Byte("23"), "23"); + map1.put(new Byte("11"), "11"); + s1Rec.setMap1(map1); + + java.util.TreeMap m1 = new java.util.TreeMap(); + java.util.TreeMap m2 = new java.util.TreeMap(); + m1.put(new Integer(5), 5L); + m1.put(new Integer(10), 10L); + m2.put(new Integer(15), 15L); + m2.put(new Integer(20), 20L); + java.util.ArrayList> vm1 = new java.util.ArrayList>(); + vm1.add(m1); + vm1.add(m2); + s1Rec.setMvec1(vm1); + java.util.ArrayList> vm2 = new java.util.ArrayList>(); + vm2.add(m1); + s1Rec.setMvec2(vm2); + + // add to our list + recsWrite.add(s1Rec); + } + + // write out to file + for (RecRecordOld rec: recsWrite) { + rec.serialize(out); + } + ostream.close(); + // write out the type info + RecRecordOld.getTypeInfo().serialize(outRTI); + oRTIstream.close(); + + // read + FileInputStream istream = new FileInputStream(tmpfile); + BinaryRecordInput in = new BinaryRecordInput(istream); + FileInputStream iRTIstream = new FileInputStream(tmpRTIfile); + BinaryRecordInput inRTI = new BinaryRecordInput(iRTIstream); + RecordTypeInfo rti = new RecordTypeInfo(); + + // read type info + rti.deserialize(inRTI); + iRTIstream.close(); + RecRecordNew.setTypeFilter(rti); + + // read records + ArrayList recsRead = new ArrayList(); + for (i=0; i> ss2Vec = s2In.getStrvec().get(j); + ArrayList> ss1Vec = s1Out.getStrvec().get(j); + for (k=0; k s2Vec = ss2Vec.get(k); + ArrayList s1Vec = ss1Vec.get(k); + for (l=0; l()); + r1.setMapVal(new TreeMap()); + r1.serialize(out, ""); + ostream.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testCsv() { + File tmpfile; + try { + tmpfile = new File("/tmp/hadooptemp.txt"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + CsvRecordOutput out = new CsvRecordOutput(ostream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + r1.serialize(out, ""); + ostream.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void testXml() { + File tmpfile; + try { + tmpfile = new File("/tmp/hadooptemp.xml"); + FileOutputStream ostream = new FileOutputStream(tmpfile); + XmlRecordOutput out = new XmlRecordOutput(ostream); + RecRecord1 r1 = new RecRecord1(); + r1.setBoolVal(true); + r1.setByteVal((byte)0x66); + r1.setFloatVal(3.145F); + r1.setDoubleVal(1.5234); + r1.setIntVal(4567); + r1.setLongVal(0x5a5a5a5a5a5aL); + r1.setStringVal("random text"); + r1.setBufferVal(new Buffer()); + r1.setVectorVal(new ArrayList()); + r1.setMapVal(new TreeMap()); + r1.serialize(out, ""); + ostream.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/test/org/apache/hadoop/security/TestAccessControlList.java b/src/test/org/apache/hadoop/security/TestAccessControlList.java new file mode 100644 index 00000000000..57c5abf875a --- /dev/null +++ b/src/test/org/apache/hadoop/security/TestAccessControlList.java @@ -0,0 +1,104 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.security; + +import java.util.Iterator; +import java.util.Set; + +import org.apache.hadoop.security.SecurityUtil.AccessControlList; + +import junit.framework.TestCase; + +public class TestAccessControlList extends TestCase { + + public void testWildCardAccessControlList() throws Exception { + AccessControlList acl; + + acl = new AccessControlList("*"); + assertTrue(acl.allAllowed()); + + acl = new AccessControlList(" * "); + assertTrue(acl.allAllowed()); + + acl = new AccessControlList(" *"); + assertTrue(acl.allAllowed()); + + acl = new AccessControlList("* "); + assertTrue(acl.allAllowed()); + } + + public void testAccessControlList() throws Exception { + AccessControlList acl; + Set users; + Set groups; + + acl = new AccessControlList("drwho tardis"); + users = acl.getUsers(); + assertEquals(users.size(), 1); + assertEquals(users.iterator().next(), "drwho"); + groups = acl.getGroups(); + assertEquals(groups.size(), 1); + assertEquals(groups.iterator().next(), "tardis"); + + acl = new AccessControlList("drwho"); + users = acl.getUsers(); + assertEquals(users.size(), 1); + assertEquals(users.iterator().next(), "drwho"); + groups = acl.getGroups(); + assertEquals(groups.size(), 0); + + acl = new AccessControlList("drwho "); + users = acl.getUsers(); + assertEquals(users.size(), 1); + assertEquals(users.iterator().next(), "drwho"); + groups = acl.getGroups(); + assertEquals(groups.size(), 0); + + acl = new AccessControlList(" tardis"); + users = acl.getUsers(); + assertEquals(users.size(), 0); + groups = acl.getGroups(); + assertEquals(groups.size(), 1); + assertEquals(groups.iterator().next(), "tardis"); + + Iterator iter; + acl = new AccessControlList("drwho,joe tardis,users"); + users = acl.getUsers(); + assertEquals(users.size(), 2); + iter = users.iterator(); + assertEquals(iter.next(), "drwho"); + assertEquals(iter.next(), "joe"); + groups = acl.getGroups(); + assertEquals(groups.size(), 2); + iter = groups.iterator(); + assertEquals(iter.next(), "tardis"); + assertEquals(iter.next(), "users"); + + acl = new AccessControlList("drwho,joe tardis, users"); + users = acl.getUsers(); + assertEquals(users.size(), 2); + iter = users.iterator(); + assertEquals(iter.next(), "drwho"); + assertEquals(iter.next(), "joe"); + groups = acl.getGroups(); + assertEquals(groups.size(), 2); + iter = groups.iterator(); + assertEquals(iter.next(), "tardis"); + assertEquals(iter.next(), "users"); + } +} diff --git a/src/test/org/apache/hadoop/security/TestAccessToken.java b/src/test/org/apache/hadoop/security/TestAccessToken.java new file mode 100644 index 00000000000..cd3cc4c482a --- /dev/null +++ b/src/test/org/apache/hadoop/security/TestAccessToken.java @@ -0,0 +1,89 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.security; + +import java.util.EnumSet; + +import org.apache.hadoop.io.TestWritable; + +import junit.framework.TestCase; + +/** Unit tests for access tokens */ +public class TestAccessToken extends TestCase { + long accessKeyUpdateInterval = 10 * 60 * 1000; // 10 mins + long accessTokenLifetime = 2 * 60 * 1000; // 2 mins + long blockID1 = 0L; + long blockID2 = 10L; + long blockID3 = -108L; + + /** test Writable */ + public void testWritable() throws Exception { + TestWritable.testWritable(ExportedAccessKeys.DUMMY_KEYS); + AccessTokenHandler handler = new AccessTokenHandler(true, + accessKeyUpdateInterval, accessTokenLifetime); + ExportedAccessKeys keys = handler.exportKeys(); + TestWritable.testWritable(keys); + TestWritable.testWritable(AccessToken.DUMMY_TOKEN); + AccessToken token = handler.generateToken(blockID3, EnumSet + .allOf(AccessTokenHandler.AccessMode.class)); + TestWritable.testWritable(token); + } + + private void tokenGenerationAndVerification(AccessTokenHandler master, + AccessTokenHandler slave) throws Exception { + // single-mode tokens + for (AccessTokenHandler.AccessMode mode : AccessTokenHandler.AccessMode + .values()) { + // generated by master + AccessToken token1 = master.generateToken(blockID1, EnumSet.of(mode)); + assertTrue(master.checkAccess(token1, null, blockID1, mode)); + assertTrue(slave.checkAccess(token1, null, blockID1, mode)); + // generated by slave + AccessToken token2 = slave.generateToken(blockID2, EnumSet.of(mode)); + assertTrue(master.checkAccess(token2, null, blockID2, mode)); + assertTrue(slave.checkAccess(token2, null, blockID2, mode)); + } + // multi-mode tokens + AccessToken mtoken = master.generateToken(blockID3, EnumSet + .allOf(AccessTokenHandler.AccessMode.class)); + for (AccessTokenHandler.AccessMode mode : AccessTokenHandler.AccessMode + .values()) { + assertTrue(master.checkAccess(mtoken, null, blockID3, mode)); + assertTrue(slave.checkAccess(mtoken, null, blockID3, mode)); + } + } + + /** test access key and token handling */ + public void testAccessTokenHandler() throws Exception { + AccessTokenHandler masterHandler = new AccessTokenHandler(true, + accessKeyUpdateInterval, accessTokenLifetime); + AccessTokenHandler slaveHandler = new AccessTokenHandler(false, + accessKeyUpdateInterval, accessTokenLifetime); + ExportedAccessKeys keys = masterHandler.exportKeys(); + slaveHandler.setKeys(keys); + tokenGenerationAndVerification(masterHandler, slaveHandler); + // key updating + masterHandler.updateKeys(); + tokenGenerationAndVerification(masterHandler, slaveHandler); + keys = masterHandler.exportKeys(); + slaveHandler.setKeys(keys); + tokenGenerationAndVerification(masterHandler, slaveHandler); + } + +} diff --git a/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java b/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java new file mode 100644 index 00000000000..51880c2d1f6 --- /dev/null +++ b/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java @@ -0,0 +1,103 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.security; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.io.TestWritable; + +import junit.framework.TestCase; + +/** Unit tests for UnixUserGroupInformation */ +public class TestUnixUserGroupInformation extends TestCase { + final private static String USER_NAME = "user1"; + final private static String GROUP1_NAME = "group1"; + final private static String GROUP2_NAME = "group2"; + final private static String GROUP3_NAME = "group3"; + final private static String[] GROUP_NAMES = + new String[]{GROUP1_NAME, GROUP2_NAME, GROUP3_NAME}; + + /** Test login method */ + public void testLogin() throws Exception { + Configuration conf = new Configuration(); + + // loin from unix + String userName = UnixUserGroupInformation.getUnixUserName(); + UnixUserGroupInformation curUserGroupInfo = + UnixUserGroupInformation.login(conf); + assertEquals(curUserGroupInfo.getUserName(), userName); + assertTrue(curUserGroupInfo == UnixUserGroupInformation.login(conf)); + + // login from the configuration + UnixUserGroupInformation userGroupInfo = new UnixUserGroupInformation( + USER_NAME, GROUP_NAMES ); + UnixUserGroupInformation.saveToConf(conf, + UnixUserGroupInformation.UGI_PROPERTY_NAME, userGroupInfo); + curUserGroupInfo = UnixUserGroupInformation.login(conf); + assertEquals(curUserGroupInfo, userGroupInfo); + assertTrue(curUserGroupInfo == UnixUserGroupInformation.login(conf)); + } + + /** test constructor */ + public void testConstructor() throws Exception { + UnixUserGroupInformation uugi = + new UnixUserGroupInformation(USER_NAME, GROUP_NAMES); + assertEquals(uugi, new UnixUserGroupInformation( new String[]{ + USER_NAME, GROUP1_NAME, GROUP2_NAME, GROUP3_NAME} )); + // failure test + testConstructorFailures(null, GROUP_NAMES); + testConstructorFailures("", GROUP_NAMES); + testConstructorFailures(USER_NAME, null); + testConstructorFailures(USER_NAME, new String[0]); + testConstructorFailures(USER_NAME, new String[]{null}); + testConstructorFailures(USER_NAME, new String[]{""}); + testConstructorFailures(USER_NAME, new String[]{GROUP1_NAME, null}); + testConstructorFailures(USER_NAME, + new String[]{GROUP1_NAME, null, GROUP2_NAME}); + } + + private void testConstructorFailures(String userName, String[] groupNames) { + boolean gotException = false; + try { + new UnixUserGroupInformation(userName, groupNames); + } catch (Exception e) { + gotException = true; + } + assertTrue(gotException); + } + + public void testEquals() throws Exception { + UnixUserGroupInformation uugi = + new UnixUserGroupInformation(USER_NAME, GROUP_NAMES); + + assertEquals(uugi, uugi); + assertEquals(uugi, new UnixUserGroupInformation(USER_NAME, GROUP_NAMES)); + assertEquals(uugi, new UnixUserGroupInformation(USER_NAME, + new String[]{GROUP1_NAME, GROUP3_NAME, GROUP2_NAME})); + assertFalse(uugi.equals(new UnixUserGroupInformation())); + assertFalse(uugi.equals(new UnixUserGroupInformation(USER_NAME, + new String[]{GROUP2_NAME, GROUP3_NAME, GROUP1_NAME}))); + } + + /** test Writable */ + public void testWritable() throws Exception { + UnixUserGroupInformation ugi = new UnixUserGroupInformation( + USER_NAME, GROUP_NAMES); + TestWritable.testWritable(ugi, new Configuration()); + } +} diff --git a/src/test/org/apache/hadoop/security/authorize/TestConfiguredPolicy.java b/src/test/org/apache/hadoop/security/authorize/TestConfiguredPolicy.java new file mode 100644 index 00000000000..203946cabd8 --- /dev/null +++ b/src/test/org/apache/hadoop/security/authorize/TestConfiguredPolicy.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.security.authorize; + +import java.security.Permission; + +import javax.security.auth.Subject; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UnixUserGroupInformation; +import org.apache.hadoop.security.SecurityUtil.AccessControlList; + +import junit.framework.TestCase; + +public class TestConfiguredPolicy extends TestCase { + private static final String USER1 = "drwho"; + private static final String USER2 = "joe"; + private static final String[] GROUPS1 = new String[]{"tardis"}; + private static final String[] GROUPS2 = new String[]{"users"}; + + private static final String KEY_1 = "test.policy.1"; + private static final String KEY_2 = "test.policy.2"; + + public static class Protocol1 { + int i; + } + public static class Protocol2 { + int j; + } + + private static class TestPolicyProvider extends PolicyProvider { + @Override + public Service[] getServices() { + return new Service[] { + new Service(KEY_1, Protocol1.class), + new Service(KEY_2, Protocol2.class), + }; + } + } + + public void testConfiguredPolicy() throws Exception { + Configuration conf = new Configuration(); + conf.set(KEY_1, AccessControlList.WILDCARD_ACL_VALUE); + conf.set(KEY_2, USER1 + " " + GROUPS1[0]); + + ConfiguredPolicy policy = new ConfiguredPolicy(conf, new TestPolicyProvider()); + SecurityUtil.setPolicy(policy); + + Subject user1 = + SecurityUtil.getSubject(new UnixUserGroupInformation(USER1, GROUPS1)); + + // Should succeed + ServiceAuthorizationManager.authorize(user1, Protocol1.class); + + // Should fail + Subject user2 = + SecurityUtil.getSubject(new UnixUserGroupInformation(USER2, GROUPS2)); + boolean failed = false; + try { + ServiceAuthorizationManager.authorize(user2, Protocol2.class); + } catch (AuthorizationException ae) { + failed = true; + } + assertTrue(failed); + } +} diff --git a/src/test/org/apache/hadoop/test/CoreTestDriver.java b/src/test/org/apache/hadoop/test/CoreTestDriver.java new file mode 100644 index 00000000000..06590c9cdf8 --- /dev/null +++ b/src/test/org/apache/hadoop/test/CoreTestDriver.java @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.test; + +import org.apache.hadoop.io.TestArrayFile; +import org.apache.hadoop.io.TestSetFile; +import org.apache.hadoop.ipc.TestIPC; +import org.apache.hadoop.ipc.TestRPC; +import org.apache.hadoop.util.ProgramDriver; + +/** + * Driver for core tests. + */ +public class CoreTestDriver { + + private ProgramDriver pgd; + + public CoreTestDriver() { + this(new ProgramDriver()); + } + + public CoreTestDriver(ProgramDriver pgd) { + this.pgd = pgd; + try { + pgd.addClass("testsetfile", TestSetFile.class, + "A test for flat files of binary key/value pairs."); + pgd.addClass("testarrayfile", TestArrayFile.class, + "A test for flat files of binary key/value pairs."); + pgd.addClass("testrpc", TestRPC.class, "A test for rpc."); + pgd.addClass("testipc", TestIPC.class, "A test for ipc."); + } catch(Throwable e) { + e.printStackTrace(); + } + } + + public void run(String argv[]) { + try { + pgd.driver(argv); + } catch(Throwable e) { + e.printStackTrace(); + } + } + + public static void main(String argv[]){ + new CoreTestDriver().run(argv); + } +} diff --git a/src/test/org/apache/hadoop/util/TestCyclicIteration.java b/src/test/org/apache/hadoop/util/TestCyclicIteration.java new file mode 100644 index 00000000000..7dfa4763e19 --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestCyclicIteration.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; + +public class TestCyclicIteration extends junit.framework.TestCase { + public void testCyclicIteration() throws Exception { + for(int n = 0; n < 5; n++) { + checkCyclicIteration(n); + } + } + + private static void checkCyclicIteration(int numOfElements) { + //create a tree map + final NavigableMap map = new TreeMap(); + final Integer[] integers = new Integer[numOfElements]; + for(int i = 0; i < integers.length; i++) { + integers[i] = 2*i; + map.put(integers[i], integers[i]); + } + System.out.println("\n\nintegers=" + Arrays.asList(integers)); + System.out.println("map=" + map); + + //try starting everywhere + for(int start = -1; start <= 2*integers.length - 1; start++) { + //get a cyclic iteration + final List iteration = new ArrayList(); + for(Map.Entry e : new CyclicIteration(map, start)) { + iteration.add(e.getKey()); + } + System.out.println("start=" + start + ", iteration=" + iteration); + + //verify results + for(int i = 0; i < integers.length; i++) { + final int j = ((start+2)/2 + i)%integers.length; + assertEquals("i=" + i + ", j=" + j, iteration.get(i), integers[j]); + } + } + } +} diff --git a/src/test/org/apache/hadoop/util/TestGenericsUtil.java b/src/test/org/apache/hadoop/util/TestGenericsUtil.java new file mode 100644 index 00000000000..af494c909d1 --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestGenericsUtil.java @@ -0,0 +1,121 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.util; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; + +public class TestGenericsUtil extends TestCase { + + public void testToArray() { + + //test a list of size 10 + List list = new ArrayList(); + + for(int i=0; i<10; i++) { + list.add(i); + } + + Integer[] arr = GenericsUtil.toArray(list); + + for (int i = 0; i < arr.length; i++) { + assertEquals(list.get(i), arr[i]); + } + } + + public void testWithEmptyList() { + try { + List list = new ArrayList(); + String[] arr = GenericsUtil.toArray(list); + fail("Empty array should throw exception"); + System.out.println(arr); //use arr so that compiler will not complain + + }catch (IndexOutOfBoundsException ex) { + //test case is successful + } + } + + public void testWithEmptyList2() { + List list = new ArrayList(); + //this method should not throw IndexOutOfBoundsException + String[] arr = GenericsUtil.toArray(String.class, list); + + assertEquals(0, arr.length); + } + + /** This class uses generics */ + private class GenericClass { + T dummy; + List list = new ArrayList(); + + void add(T item) { + list.add(item); + } + + T[] funcThatUsesToArray() { + T[] arr = GenericsUtil.toArray(list); + return arr; + } + } + + public void testWithGenericClass() { + + GenericClass testSubject = new GenericClass(); + + testSubject.add("test1"); + testSubject.add("test2"); + + try { + //this cast would fail, if we had not used GenericsUtil.toArray, since the + //rmethod would return Object[] rather than String[] + String[] arr = testSubject.funcThatUsesToArray(); + + assertEquals("test1", arr[0]); + assertEquals("test2", arr[1]); + + }catch (ClassCastException ex) { + fail("GenericsUtil#toArray() is not working for generic classes"); + } + + } + + public void testGenericOptionsParser() throws Exception { + GenericOptionsParser parser = new GenericOptionsParser( + new Configuration(), new String[] {"-jt"}); + assertEquals(parser.getRemainingArgs().length, 0); + } + + public void testGetClass() { + + //test with Integer + Integer x = new Integer(42); + Class c = GenericsUtil.getClass(x); + assertEquals(Integer.class, c); + + //test with GenericClass + GenericClass testSubject = new GenericClass(); + Class> c2 = GenericsUtil.getClass(testSubject); + assertEquals(GenericClass.class, c2); + } + +} diff --git a/src/test/org/apache/hadoop/util/TestIndexedSort.java b/src/test/org/apache/hadoop/util/TestIndexedSort.java new file mode 100644 index 00000000000..d806a0adce9 --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestIndexedSort.java @@ -0,0 +1,361 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.util; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.io.DataInputBuffer; +import org.apache.hadoop.io.DataOutputBuffer; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.WritableComparator; + +public class TestIndexedSort extends TestCase { + + public void sortAllEqual(IndexedSorter sorter) throws Exception { + final int SAMPLE = 500; + int[] values = new int[SAMPLE]; + Arrays.fill(values, 10); + SampleSortable s = new SampleSortable(values); + sorter.sort(s, 0, SAMPLE); + int[] check = s.getSorted(); + assertTrue(Arrays.toString(values) + "\ndoesn't match\n" + + Arrays.toString(check), Arrays.equals(values, check)); + // Set random min/max, re-sort. + Random r = new Random(); + int min = r.nextInt(SAMPLE); + int max = (min + 1 + r.nextInt(SAMPLE - 2)) % SAMPLE; + values[min] = 9; + values[max] = 11; + System.out.println("testAllEqual setting min/max at " + min + "/" + max + + "(" + sorter.getClass().getName() + ")"); + s = new SampleSortable(values); + sorter.sort(s, 0, SAMPLE); + check = s.getSorted(); + Arrays.sort(values); + assertTrue(check[0] == 9); + assertTrue(check[SAMPLE - 1] == 11); + assertTrue(Arrays.toString(values) + "\ndoesn't match\n" + + Arrays.toString(check), Arrays.equals(values, check)); + } + + public void sortSorted(IndexedSorter sorter) throws Exception { + final int SAMPLE = 500; + int[] values = new int[SAMPLE]; + Random r = new Random(); + long seed = r.nextLong(); + r.setSeed(seed); + System.out.println("testSorted seed: " + seed + + "(" + sorter.getClass().getName() + ")"); + for (int i = 0; i < SAMPLE; ++i) { + values[i] = r.nextInt(100); + } + Arrays.sort(values); + SampleSortable s = new SampleSortable(values); + sorter.sort(s, 0, SAMPLE); + int[] check = s.getSorted(); + assertTrue(Arrays.toString(values) + "\ndoesn't match\n" + + Arrays.toString(check), Arrays.equals(values, check)); + } + + public void sortSequential(IndexedSorter sorter) throws Exception { + final int SAMPLE = 500; + int[] values = new int[SAMPLE]; + for (int i = 0; i < SAMPLE; ++i) { + values[i] = i; + } + SampleSortable s = new SampleSortable(values); + sorter.sort(s, 0, SAMPLE); + int[] check = s.getSorted(); + assertTrue(Arrays.toString(values) + "\ndoesn't match\n" + + Arrays.toString(check), Arrays.equals(values, check)); + } + + public void sortSingleRecord(IndexedSorter sorter) throws Exception { + final int SAMPLE = 1; + SampleSortable s = new SampleSortable(SAMPLE); + int[] values = s.getValues(); + sorter.sort(s, 0, SAMPLE); + int[] check = s.getSorted(); + assertTrue(Arrays.toString(values) + "\ndoesn't match\n" + + Arrays.toString(check), Arrays.equals(values, check)); + } + + public void sortRandom(IndexedSorter sorter) throws Exception { + final int SAMPLE = 256 * 1024; + SampleSortable s = new SampleSortable(SAMPLE); + long seed = s.getSeed(); + System.out.println("sortRandom seed: " + seed + + "(" + sorter.getClass().getName() + ")"); + int[] values = s.getValues(); + Arrays.sort(values); + sorter.sort(s, 0, SAMPLE); + int[] check = s.getSorted(); + assertTrue("seed: " + seed + "\ndoesn't match\n", + Arrays.equals(values, check)); + } + + public void sortWritable(IndexedSorter sorter) throws Exception { + final int SAMPLE = 1000; + WritableSortable s = new WritableSortable(SAMPLE); + long seed = s.getSeed(); + System.out.println("sortWritable seed: " + seed + + "(" + sorter.getClass().getName() + ")"); + String[] values = s.getValues(); + Arrays.sort(values); + sorter.sort(s, 0, SAMPLE); + String[] check = s.getSorted(); + assertTrue("seed: " + seed + "\ndoesn't match", + Arrays.equals(values, check)); + } + + + public void testQuickSort() throws Exception { + QuickSort sorter = new QuickSort(); + sortRandom(sorter); + sortSingleRecord(sorter); + sortSequential(sorter); + sortSorted(sorter); + sortAllEqual(sorter); + sortWritable(sorter); + + // test degenerate case for median-of-three partitioning + // a_n, a_1, a_2, ..., a_{n-1} + final int DSAMPLE = 500; + int[] values = new int[DSAMPLE]; + for (int i = 0; i < DSAMPLE; ++i) { values[i] = i; } + values[0] = values[DSAMPLE - 1] + 1; + SampleSortable s = new SampleSortable(values); + values = s.getValues(); + final int DSS = (DSAMPLE / 2) * (DSAMPLE / 2); + // Worst case is (N/2)^2 comparisons, not including those effecting + // the median-of-three partitioning; impl should handle this case + MeasuredSortable m = new MeasuredSortable(s, DSS); + sorter.sort(m, 0, DSAMPLE); + System.out.println("QuickSort degen cmp/swp: " + + m.getCmp() + "/" + m.getSwp() + + "(" + sorter.getClass().getName() + ")"); + Arrays.sort(values); + int[] check = s.getSorted(); + assertTrue(Arrays.equals(values, check)); + } + + public void testHeapSort() throws Exception { + HeapSort sorter = new HeapSort(); + sortRandom(sorter); + sortSingleRecord(sorter); + sortSequential(sorter); + sortSorted(sorter); + sortAllEqual(sorter); + sortWritable(sorter); + } + + // Sortables // + + private static class SampleSortable implements IndexedSortable { + private int[] valindex; + private int[] valindirect; + private int[] values; + private final long seed; + + public SampleSortable() { + this(50); + } + + public SampleSortable(int j) { + Random r = new Random(); + seed = r.nextLong(); + r.setSeed(seed); + values = new int[j]; + valindex = new int[j]; + valindirect = new int[j]; + for (int i = 0; i < j; ++i) { + valindex[i] = valindirect[i] = i; + values[i] = r.nextInt(1000); + } + } + + public SampleSortable(int[] values) { + this.values = values; + valindex = new int[values.length]; + valindirect = new int[values.length]; + for (int i = 0; i < values.length; ++i) { + valindex[i] = valindirect[i] = i; + } + seed = 0; + } + + public long getSeed() { + return seed; + } + + public int compare(int i, int j) { + // assume positive + return + values[valindirect[valindex[i]]] - values[valindirect[valindex[j]]]; + } + + public void swap(int i, int j) { + int tmp = valindex[i]; + valindex[i] = valindex[j]; + valindex[j] = tmp; + } + + public int[] getSorted() { + int[] ret = new int[values.length]; + for (int i = 0; i < ret.length; ++i) { + ret[i] = values[valindirect[valindex[i]]]; + } + return ret; + } + + public int[] getValues() { + int[] ret = new int[values.length]; + System.arraycopy(values, 0, ret, 0, values.length); + return ret; + } + + } + + public static class MeasuredSortable implements IndexedSortable { + + private int comparisions; + private int swaps; + private final int maxcmp; + private final int maxswp; + private IndexedSortable s; + + public MeasuredSortable(IndexedSortable s) { + this(s, Integer.MAX_VALUE); + } + + public MeasuredSortable(IndexedSortable s, int maxcmp) { + this(s, maxcmp, Integer.MAX_VALUE); + } + + public MeasuredSortable(IndexedSortable s, int maxcmp, int maxswp) { + this.s = s; + this.maxcmp = maxcmp; + this.maxswp = maxswp; + } + + public int getCmp() { return comparisions; } + public int getSwp() { return swaps; } + + public int compare(int i, int j) { + assertTrue("Expected fewer than " + maxcmp + " comparisons", + ++comparisions < maxcmp); + return s.compare(i, j); + } + + public void swap(int i, int j) { + assertTrue("Expected fewer than " + maxswp + " swaps", + ++swaps < maxswp); + s.swap(i, j); + } + + } + + private static class WritableSortable implements IndexedSortable { + + private static Random r = new Random(); + private final int eob; + private final int[] indices; + private final int[] offsets; + private final byte[] bytes; + private final WritableComparator comparator; + private final String[] check; + private final long seed; + + public WritableSortable() throws IOException { + this(100); + } + + public WritableSortable(int j) throws IOException { + seed = r.nextLong(); + r.setSeed(seed); + Text t = new Text(); + StringBuffer sb = new StringBuffer(); + indices = new int[j]; + offsets = new int[j]; + check = new String[j]; + DataOutputBuffer dob = new DataOutputBuffer(); + for (int i = 0; i < j; ++i) { + indices[i] = i; + offsets[i] = dob.getLength(); + genRandom(t, r.nextInt(15) + 1, sb); + t.write(dob); + check[i] = t.toString(); + } + eob = dob.getLength(); + bytes = dob.getData(); + comparator = WritableComparator.get(Text.class); + } + + public long getSeed() { + return seed; + } + + private static void genRandom(Text t, int len, StringBuffer sb) { + sb.setLength(0); + for (int i = 0; i < len; ++i) { + sb.append(Integer.toString(r.nextInt(26) + 10, 36)); + } + t.set(sb.toString()); + } + + public int compare(int i, int j) { + final int ii = indices[i]; + final int ij = indices[j]; + return comparator.compare(bytes, offsets[ii], + ((ii + 1 == indices.length) ? eob : offsets[ii + 1]) - offsets[ii], + bytes, offsets[ij], + ((ij + 1 == indices.length) ? eob : offsets[ij + 1]) - offsets[ij]); + } + + public void swap(int i, int j) { + int tmp = indices[i]; + indices[i] = indices[j]; + indices[j] = tmp; + } + + public String[] getValues() { + return check; + } + + public String[] getSorted() throws IOException { + String[] ret = new String[indices.length]; + Text t = new Text(); + DataInputBuffer dib = new DataInputBuffer(); + for (int i = 0; i < ret.length; ++i) { + int ii = indices[i]; + dib.reset(bytes, offsets[ii], + ((ii + 1 == indices.length) ? eob : offsets[ii + 1]) - offsets[ii]); + t.readFields(dib); + ret[i] = t.toString(); + } + return ret; + } + + } + +} diff --git a/src/test/org/apache/hadoop/util/TestProcfsBasedProcessTree.java b/src/test/org/apache/hadoop/util/TestProcfsBasedProcessTree.java new file mode 100644 index 00000000000..0b975074026 --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestProcfsBasedProcessTree.java @@ -0,0 +1,234 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Random; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.util.Shell.ExitCodeException; +import org.apache.hadoop.util.Shell.ShellCommandExecutor; + +import junit.framework.TestCase; + +/** + * A JUnit test to test ProcfsBasedProcessTree. + */ +public class TestProcfsBasedProcessTree extends TestCase { + + private static final Log LOG = LogFactory + .getLog(TestProcfsBasedProcessTree.class); + private static String TEST_ROOT_DIR = new Path(System.getProperty( + "test.build.data", "/tmp")).toString().replace(' ', '+'); + + private ShellCommandExecutor shexec = null; + private String pidFile, lowestDescendant; + private String shellScript; + private static final int N = 6; // Controls the RogueTask + + private class RogueTaskThread extends Thread { + public void run() { + try { + Vector args = new Vector(); + if(ProcessTree.isSetsidAvailable) { + args.add("setsid"); + } + args.add("bash"); + args.add("-c"); + args.add(" echo $$ > " + pidFile + "; sh " + + shellScript + " " + N + ";") ; + shexec = new ShellCommandExecutor(args.toArray(new String[0])); + shexec.execute(); + } catch (ExitCodeException ee) { + LOG.info("Shell Command exit with a non-zero exit code. This is" + + " expected as we are killing the subprocesses of the" + + " task intentionally. " + ee); + } catch (IOException ioe) { + LOG.info("Error executing shell command " + ioe); + } finally { + LOG.info("Exit code: " + shexec.getExitCode()); + } + } + } + + private String getRogueTaskPID() { + File f = new File(pidFile); + while (!f.exists()) { + try { + Thread.sleep(500); + } catch (InterruptedException ie) { + break; + } + } + + // read from pidFile + return getPidFromPidFile(pidFile); + } + + public void testProcessTree() { + + try { + if (!ProcfsBasedProcessTree.isAvailable()) { + System.out + .println("ProcfsBasedProcessTree is not available on this system. Not testing"); + return; + } + } catch (Exception e) { + LOG.info(StringUtils.stringifyException(e)); + return; + } + // create shell script + Random rm = new Random(); + File tempFile = new File(TEST_ROOT_DIR, this.getName() + "_shellScript_" + + rm.nextInt() + ".sh"); + tempFile.deleteOnExit(); + shellScript = TEST_ROOT_DIR + File.separator + tempFile.getName(); + + // create pid file + tempFile = new File(TEST_ROOT_DIR, this.getName() + "_pidFile_" + + rm.nextInt() + ".pid"); + tempFile.deleteOnExit(); + pidFile = TEST_ROOT_DIR + File.separator + tempFile.getName(); + + lowestDescendant = TEST_ROOT_DIR + File.separator + "lowestDescendantPidFile"; + + // write to shell-script + try { + FileWriter fWriter = new FileWriter(shellScript); + fWriter.write( + "# rogue task\n" + + "sleep 1\n" + + "echo hello\n" + + "if [ $1 -ne 0 ]\n" + + "then\n" + + " sh " + shellScript + " $(($1-1))\n" + + "else\n" + + " echo $$ > " + lowestDescendant + "\n" + + " while true\n do\n" + + " sleep 5\n" + + " done\n" + + "fi"); + fWriter.close(); + } catch (IOException ioe) { + LOG.info("Error: " + ioe); + return; + } + + Thread t = new RogueTaskThread(); + t.start(); + String pid = getRogueTaskPID(); + LOG.info("Root process pid: " + pid); + ProcfsBasedProcessTree p = new ProcfsBasedProcessTree(pid, + ProcessTree.isSetsidAvailable, + ProcessTree.DEFAULT_SLEEPTIME_BEFORE_SIGKILL); + p = p.getProcessTree(); // initialize + LOG.info("ProcessTree: " + p.toString()); + + File leaf = new File(lowestDescendant); + //wait till lowest descendant process of Rougue Task starts execution + while (!leaf.exists()) { + try { + Thread.sleep(500); + } catch (InterruptedException ie) { + break; + } + } + + p = p.getProcessTree(); // reconstruct + LOG.info("ProcessTree: " + p.toString()); + + // destroy the map task and all its subprocesses + p.destroy(true/*in the background*/); + + if(ProcessTree.isSetsidAvailable) {// whole processtree should be gone + assertEquals(false, p.isAnyProcessInTreeAlive()); + } + else {// process should be gone + assertFalse("ProcessTree must have been gone", p.isAlive()); + } + // Not able to join thread sometimes when forking with large N. + try { + t.join(2000); + LOG.info("RogueTaskThread successfully joined."); + } catch (InterruptedException ie) { + LOG.info("Interrupted while joining RogueTaskThread."); + } + + // ProcessTree is gone now. Any further calls should be sane. + p = p.getProcessTree(); + assertFalse("ProcessTree must have been gone", p.isAlive()); + assertTrue("Cumulative vmem for the gone-process is " + + p.getCumulativeVmem() + " . It should be zero.", p + .getCumulativeVmem() == 0); + assertTrue(p.toString().equals("[ ]")); + } + + /** + * Get PID from a pid-file. + * + * @param pidFileName + * Name of the pid-file. + * @return the PID string read from the pid-file. Returns null if the + * pidFileName points to a non-existing file or if read fails from the + * file. + */ + public static String getPidFromPidFile(String pidFileName) { + BufferedReader pidFile = null; + FileReader fReader = null; + String pid = null; + + try { + fReader = new FileReader(pidFileName); + pidFile = new BufferedReader(fReader); + } catch (FileNotFoundException f) { + LOG.debug("PidFile doesn't exist : " + pidFileName); + return pid; + } + + try { + pid = pidFile.readLine(); + } catch (IOException i) { + LOG.error("Failed to read from " + pidFileName); + } finally { + try { + if (fReader != null) { + fReader.close(); + } + try { + if (pidFile != null) { + pidFile.close(); + } + } catch (IOException i) { + LOG.warn("Error closing the stream " + pidFile); + } + } catch (IOException i) { + LOG.warn("Error closing the stream " + fReader); + } + } + return pid; + } +} diff --git a/src/test/org/apache/hadoop/util/TestShell.java b/src/test/org/apache/hadoop/util/TestShell.java new file mode 100644 index 00000000000..ca7303187bc --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestShell.java @@ -0,0 +1,88 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.util; + +import junit.framework.TestCase; + +import java.io.BufferedReader; +import java.io.IOException; + +public class TestShell extends TestCase { + + private static class Command extends Shell { + private int runCount = 0; + + private Command(long interval) { + super(interval); + } + + protected String[] getExecString() { + return new String[] {"echo", "hello"}; + } + + protected void parseExecResult(BufferedReader lines) throws IOException { + ++runCount; + } + + public int getRunCount() { + return runCount; + } + } + + public void testInterval() throws IOException { + testInterval(Long.MIN_VALUE / 60000); // test a negative interval + testInterval(0L); // test a zero interval + testInterval(10L); // interval equal to 10mins + testInterval(System.currentTimeMillis() / 60000 + 60); // test a very big interval + } + + /** + * Assert that a string has a substring in it + * @param string string to search + * @param search what to search for it + */ + private void assertInString(String string, String search) { + assertNotNull("Empty String", string); + if (!string.contains(search)) { + fail("Did not find \"" + search + "\" in " + string); + } + } + + public void testShellCommandExecutorToString() throws Throwable { + Shell.ShellCommandExecutor sce=new Shell.ShellCommandExecutor( + new String[] { "ls","..","arg 2"}); + String command = sce.toString(); + assertInString(command,"ls"); + assertInString(command, " .. "); + assertInString(command, "\"arg 2\""); + } + + private void testInterval(long interval) throws IOException { + Command command = new Command(interval); + + command.run(); + assertEquals(1, command.getRunCount()); + + command.run(); + if (interval > 0) { + assertEquals(1, command.getRunCount()); + } else { + assertEquals(2, command.getRunCount()); + } + } +} diff --git a/src/test/org/apache/hadoop/util/TestStringUtils.java b/src/test/org/apache/hadoop/util/TestStringUtils.java new file mode 100644 index 00000000000..e68609ae2ff --- /dev/null +++ b/src/test/org/apache/hadoop/util/TestStringUtils.java @@ -0,0 +1,121 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.util; + +import junit.framework.TestCase; + +public class TestStringUtils extends TestCase { + final private static String NULL_STR = null; + final private static String EMPTY_STR = ""; + final private static String STR_WO_SPECIAL_CHARS = "AB"; + final private static String STR_WITH_COMMA = "A,B"; + final private static String ESCAPED_STR_WITH_COMMA = "A\\,B"; + final private static String STR_WITH_ESCAPE = "AB\\"; + final private static String ESCAPED_STR_WITH_ESCAPE = "AB\\\\"; + final private static String STR_WITH_BOTH2 = ",A\\,,B\\\\,"; + final private static String ESCAPED_STR_WITH_BOTH2 = + "\\,A\\\\\\,\\,B\\\\\\\\\\,"; + + public void testEscapeString() throws Exception { + assertEquals(NULL_STR, StringUtils.escapeString(NULL_STR)); + assertEquals(EMPTY_STR, StringUtils.escapeString(EMPTY_STR)); + assertEquals(STR_WO_SPECIAL_CHARS, + StringUtils.escapeString(STR_WO_SPECIAL_CHARS)); + assertEquals(ESCAPED_STR_WITH_COMMA, + StringUtils.escapeString(STR_WITH_COMMA)); + assertEquals(ESCAPED_STR_WITH_ESCAPE, + StringUtils.escapeString(STR_WITH_ESCAPE)); + assertEquals(ESCAPED_STR_WITH_BOTH2, + StringUtils.escapeString(STR_WITH_BOTH2)); + } + + public void testSplit() throws Exception { + assertEquals(NULL_STR, StringUtils.split(NULL_STR)); + String[] splits = StringUtils.split(EMPTY_STR); + assertEquals(0, splits.length); + splits = StringUtils.split(",,"); + assertEquals(0, splits.length); + splits = StringUtils.split(STR_WO_SPECIAL_CHARS); + assertEquals(1, splits.length); + assertEquals(STR_WO_SPECIAL_CHARS, splits[0]); + splits = StringUtils.split(STR_WITH_COMMA); + assertEquals(2, splits.length); + assertEquals("A", splits[0]); + assertEquals("B", splits[1]); + splits = StringUtils.split(ESCAPED_STR_WITH_COMMA); + assertEquals(1, splits.length); + assertEquals(ESCAPED_STR_WITH_COMMA, splits[0]); + splits = StringUtils.split(STR_WITH_ESCAPE); + assertEquals(1, splits.length); + assertEquals(STR_WITH_ESCAPE, splits[0]); + splits = StringUtils.split(STR_WITH_BOTH2); + assertEquals(3, splits.length); + assertEquals(EMPTY_STR, splits[0]); + assertEquals("A\\,", splits[1]); + assertEquals("B\\\\", splits[2]); + splits = StringUtils.split(ESCAPED_STR_WITH_BOTH2); + assertEquals(1, splits.length); + assertEquals(ESCAPED_STR_WITH_BOTH2, splits[0]); + } + + public void testUnescapeString() throws Exception { + assertEquals(NULL_STR, StringUtils.unEscapeString(NULL_STR)); + assertEquals(EMPTY_STR, StringUtils.unEscapeString(EMPTY_STR)); + assertEquals(STR_WO_SPECIAL_CHARS, + StringUtils.unEscapeString(STR_WO_SPECIAL_CHARS)); + try { + StringUtils.unEscapeString(STR_WITH_COMMA); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + assertEquals(STR_WITH_COMMA, + StringUtils.unEscapeString(ESCAPED_STR_WITH_COMMA)); + try { + StringUtils.unEscapeString(STR_WITH_ESCAPE); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + assertEquals(STR_WITH_ESCAPE, + StringUtils.unEscapeString(ESCAPED_STR_WITH_ESCAPE)); + try { + StringUtils.unEscapeString(STR_WITH_BOTH2); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + assertEquals(STR_WITH_BOTH2, + StringUtils.unEscapeString(ESCAPED_STR_WITH_BOTH2)); + } + + public void testTraditionalBinaryPrefix() throws Exception { + String[] symbol = {"k", "m", "g", "t", "p", "e"}; + long m = 1024; + for(String s : symbol) { + assertEquals(0, StringUtils.TraditionalBinaryPrefix.string2long(0 + s)); + assertEquals(m, StringUtils.TraditionalBinaryPrefix.string2long(1 + s)); + m *= 1024; + } + + assertEquals(0L, StringUtils.TraditionalBinaryPrefix.string2long("0")); + assertEquals(-1259520L, StringUtils.TraditionalBinaryPrefix.string2long("-1230k")); + assertEquals(956703965184L, StringUtils.TraditionalBinaryPrefix.string2long("891g")); + } +}

C#)8rr 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諾#)mfmh&bX}BR;SYb#LtJ17ndP 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%0D?@wm4oMxuoq1Raq! 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