zookeeperOver.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  2. <html>
  3. <head>
  4. <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <meta content="Apache Forrest" name="Generator">
  6. <meta name="Forrest-version" content="0.8">
  7. <meta name="Forrest-skin-name" content="pelt">
  8. <title></title>
  9. <link type="text/css" href="skin/basic.css" rel="stylesheet">
  10. <link media="screen" type="text/css" href="skin/screen.css" rel="stylesheet">
  11. <link media="print" type="text/css" href="skin/print.css" rel="stylesheet">
  12. <link type="text/css" href="skin/profile.css" rel="stylesheet">
  13. <script src="skin/getBlank.js" language="javascript" type="text/javascript"></script><script src="skin/getMenu.js" language="javascript" type="text/javascript"></script><script src="skin/fontsize.js" language="javascript" type="text/javascript"></script>
  14. <link rel="shortcut icon" href="images/favicon.ico">
  15. </head>
  16. <body onload="init()">
  17. <script type="text/javascript">ndeSetTextSize();</script>
  18. <div id="top">
  19. <!--+
  20. |breadtrail
  21. +-->
  22. <div class="breadtrail">
  23. <a href="http://www.apache.org/">Apache</a> &gt; <a href="http://hadoop.apache.org/">Hadoop</a> &gt; <a href="http://hadoop.apache.org/zookeeper/">ZooKeeper</a><script src="skin/breadcrumbs.js" language="JavaScript" type="text/javascript"></script>
  24. </div>
  25. <!--+
  26. |header
  27. +-->
  28. <div class="header">
  29. <!--+
  30. |start group logo
  31. +-->
  32. <div class="grouplogo">
  33. <a href="http://hadoop.apache.org/"><img class="logoImage" alt="Hadoop" src="images/hadoop-logo.jpg" title="Apache Hadoop"></a>
  34. </div>
  35. <!--+
  36. |end group logo
  37. +-->
  38. <!--+
  39. |start Project Logo
  40. +-->
  41. <div class="projectlogo">
  42. <a href="http://hadoop.apache.org/zookeeper/"><img class="logoImage" alt="ZooKeeper" src="images/zookeeper_small.gif" title="The Hadoop database"></a>
  43. </div>
  44. <!--+
  45. |end Project Logo
  46. +-->
  47. <!--+
  48. |start Search
  49. +-->
  50. <div class="searchbox">
  51. <form action="http://www.google.com/search" method="get" class="roundtopsmall">
  52. <input value="hadoop.apache.org" name="sitesearch" type="hidden"><input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp;
  53. <input name="Search" value="Search" type="submit">
  54. </form>
  55. </div>
  56. <!--+
  57. |end search
  58. +-->
  59. <!--+
  60. |start Tabs
  61. +-->
  62. <ul id="tabs">
  63. <li>
  64. <a class="unselected" href="http://hadoop.apache.org/zookeeper/">Project</a>
  65. </li>
  66. <li>
  67. <a class="unselected" href="http://wiki.apache.org/hadoop/ZooKeeper">Wiki</a>
  68. </li>
  69. <li class="current">
  70. <a class="selected" href="index.html">ZooKeeper Documentation</a>
  71. </li>
  72. </ul>
  73. <!--+
  74. |end Tabs
  75. +-->
  76. </div>
  77. </div>
  78. <div id="main">
  79. <div id="publishedStrip">
  80. <!--+
  81. |start Subtabs
  82. +-->
  83. <div id="level2tabs"></div>
  84. <!--+
  85. |end Endtabs
  86. +-->
  87. <script type="text/javascript"><!--
  88. document.write("Last Published: " + document.lastModified);
  89. // --></script>
  90. </div>
  91. <!--+
  92. |breadtrail
  93. +-->
  94. <div class="breadtrail">
  95. &nbsp;
  96. </div>
  97. <!--+
  98. |start Menu, mainarea
  99. +-->
  100. <!--+
  101. |start Menu
  102. +-->
  103. <div id="menu">
  104. <div onclick="SwitchMenu('menu_selected_1.1', 'skin/')" id="menu_selected_1.1Title" class="menutitle" style="background-image: url('skin/images/chapter_open.gif');">Documentation</div>
  105. <div id="menu_selected_1.1" class="selectedmenuitemgroup" style="display: block;">
  106. <div class="menuitem">
  107. <a href="index.html">Welcome</a>
  108. </div>
  109. <div class="menupage">
  110. <div class="menupagetitle">Zookeeper Overview</div>
  111. </div>
  112. <div class="menuitem">
  113. <a href="zookeeperStarted.html">Getting Started</a>
  114. </div>
  115. <div class="menuitem">
  116. <a href="zookeeperProgrammers.html">Programmer's Guide</a>
  117. </div>
  118. <div class="menuitem">
  119. <a href="recipes.html">Recipes</a>
  120. </div>
  121. <div class="menuitem">
  122. <a href="zookeeperAdmin.html">Administrator's Guide</a>
  123. </div>
  124. <div class="menuitem">
  125. <a href="api/index.html">API Docs</a>
  126. </div>
  127. <div class="menuitem">
  128. <a href="http://wiki.apache.org/hadoop/ZooKeeper">Wiki</a>
  129. </div>
  130. <div class="menuitem">
  131. <a href="http://wiki.apache.org/hadoop/ZooKeeper/FAQ">FAQ</a>
  132. </div>
  133. <div class="menuitem">
  134. <a href="http://hadoop.apache.org/zookeeper/mailing_lists.html">Mailing Lists</a>
  135. </div>
  136. <div class="menuitem">
  137. <a href="zookeeperOtherInfo.html">Other Info</a>
  138. </div>
  139. </div>
  140. <div id="credit"></div>
  141. <div id="roundbottom">
  142. <img style="display: none" class="corner" height="15" width="15" alt="" src="skin/images/rc-b-l-15-1body-2menu-3menu.png"></div>
  143. <!--+
  144. |alternative credits
  145. +-->
  146. <div id="credit2"></div>
  147. </div>
  148. <!--+
  149. |end Menu
  150. +-->
  151. <!--+
  152. |start content
  153. +-->
  154. <div id="content">
  155. <div title="Portable Document Format" class="pdflink">
  156. <a class="dida" href="zookeeperOver.pdf"><img alt="PDF -icon" src="skin/images/pdfdoc.gif" class="skin"><br>
  157. PDF</a>
  158. </div>
  159. <div id="minitoc-area">
  160. <ul class="minitoc">
  161. <li>
  162. <a href="#ZooKeeper%3A+A+Distributed+Coordination+Service+for+Distributed%0A++++Applications">ZooKeeper: A Distributed Coordination Service for Distributed
  163. Applications</a>
  164. <ul class="minitoc">
  165. <li>
  166. <a href="#sc_designGoals">Design Goals</a>
  167. </li>
  168. <li>
  169. <a href="#sc_dataModelNameSpace">Data model and the hierarchical namespace</a>
  170. </li>
  171. <li>
  172. <a href="#Nodes+and+ephemeral+nodes">Nodes and ephemeral nodes</a>
  173. </li>
  174. <li>
  175. <a href="#Conditional+updates+and+watches">Conditional updates and watches</a>
  176. </li>
  177. <li>
  178. <a href="#Guarantees">Guarantees</a>
  179. </li>
  180. <li>
  181. <a href="#Simple+API">Simple API</a>
  182. </li>
  183. <li>
  184. <a href="#Implementation">Implementation</a>
  185. </li>
  186. <li>
  187. <a href="#Uses">Uses</a>
  188. </li>
  189. <li>
  190. <a href="#Performance">Performance</a>
  191. </li>
  192. <li>
  193. <a href="#The+ZooKeeper+Project">The ZooKeeper Project</a>
  194. </li>
  195. </ul>
  196. </li>
  197. </ul>
  198. </div>
  199. <title>ZooKeeper</title>
  200. <a name="N1000A"></a><a name="ZooKeeper%3A+A+Distributed+Coordination+Service+for+Distributed%0A++++Applications"></a>
  201. <h2 class="h3">ZooKeeper: A Distributed Coordination Service for Distributed
  202. Applications</h2>
  203. <div class="section">
  204. <p>ZooKeeper is a distributed, open-source coordination service for
  205. distributed applications. It exposes a simple set of primitives that
  206. distributed applications can build upon to implement higher level services
  207. for synchronization, configuration maintenance, and groups and naming. It
  208. is designed to be easy to program to, and uses a data model styled after
  209. the familiar directory tree structure of file systems. It runs in Java and
  210. has bindings for both Java and C.</p>
  211. <p>Coordination services are notoriously hard to get right. They are
  212. especially prone to errors such as race conditions and deadlock. The
  213. motivation behind ZooKeeper is to relieve distributed applications the
  214. responsibility of implementing coordination services from scratch.</p>
  215. <a name="N10016"></a><a name="sc_designGoals"></a>
  216. <h3 class="h4">Design Goals</h3>
  217. <p>
  218. <strong>ZooKeeper is simple.</strong> ZooKeeper
  219. allows distributed processes to coordinate with each other through a
  220. shared hierarchal namespace which is organized similarly to a standard
  221. file system. The name space consists of data registers - called znodes,
  222. in ZooKeeper parlance - and these are similar to files and directories.
  223. Unlike a typical file system, which is designed for storage, ZooKeeper
  224. data is kept in-memory, which means ZooKeeper can acheive high
  225. throughput and low latency numbers.</p>
  226. <p>The ZooKeeper implementation puts a premium on high performance,
  227. highly available, strictly ordered access. The performance aspects of
  228. ZooKeeper means it can be used in large, distributed systems. The
  229. reliability aspects keep it from being a single point of failure. The
  230. strict ordering means that sophisticated synchronization primitives can
  231. be implemented at the client.</p>
  232. <p>
  233. <strong>ZooKeeper is replicated.</strong> Like the
  234. distributed processes it coordinates, ZooKeeper itself is intended to be
  235. replicated over a sets of machines called quorums.</p>
  236. <table class="ForrestTable" cellspacing="1" cellpadding="4">
  237. <tr>
  238. <td>ZooKeeper Service</td>
  239. </tr>
  240. <tr>
  241. <td>
  242. <img alt="" src="images/zkservice.jpg">
  243. </td>
  244. </tr>
  245. </table>
  246. <p>The servers that make up the ZooKeeper service must all know about
  247. each other. They maintain an in-memory image of state, along with a
  248. transaction logs and snapshots in a persistent store. As long as a
  249. majority of the servers are available, the ZooKeeper service will be
  250. available.</p>
  251. <p>Clients connect to a single ZooKeeper server. The client maintains
  252. a TCP connection through which it sends requests, gets responses, gets
  253. watch events, and sends heart beats. If the TCP connection to the server
  254. breaks, the client will connect to a different server.</p>
  255. <p>
  256. <strong>ZooKeeper is ordered.</strong> ZooKeeper
  257. stamps each update with a number that reflects the order of all
  258. ZooKeeper transactions. Subsequent operations can use the order to
  259. implement higher-level abstractions, such as synchronization
  260. primitives.</p>
  261. <p>
  262. <strong>ZooKeeper is fast.</strong> It is
  263. especially fast in "read-dominant" workloads. ZooKeeper applications run
  264. on thousands of machines, and it performs best where reads are more
  265. common than writes, at ratios of around 10:1.</p>
  266. <a name="N10046"></a><a name="sc_dataModelNameSpace"></a>
  267. <h3 class="h4">Data model and the hierarchical namespace</h3>
  268. <p>The name space provided by ZooKeeper is much like that of a
  269. standard file system. A name is a sequence of path elements separated by
  270. a slash (/). Every node in ZooKeeper's name space is identified by a
  271. path.</p>
  272. <table class="ForrestTable" cellspacing="1" cellpadding="4">
  273. <tr>
  274. <td>ZooKeeper's Hierarchical Namespace</td>
  275. </tr>
  276. <tr>
  277. <td>
  278. <img alt="" src="images/zknamespace.jpg">
  279. </td>
  280. </tr>
  281. </table>
  282. <a name="N1005C"></a><a name="Nodes+and+ephemeral+nodes"></a>
  283. <h3 class="h4">Nodes and ephemeral nodes</h3>
  284. <p>Unlike is standard file systems, each node in a ZooKeeper
  285. namespace can have data associated with it as well as children. It is
  286. like having a file-system that allows a file to also be a directory.
  287. (ZooKeeper was designed to store coordination data: status information,
  288. configuration, location information, etc., so the data stored at each
  289. node is usually small, in the byte to kilobyte range.) We use the term
  290. <em>znode</em> to make it clear that we are talking about
  291. ZooKeeper data nodes.</p>
  292. <p>Znodes maintain a stat structure that includes version numbers for
  293. data changes, ACL changes, and timestamps, to allow cache validations
  294. and coordinated updates. Each time a znode's data changes, the version
  295. number increases. For instance, whenever a client retrieves data it also
  296. receives the version of the data.</p>
  297. <p>The data stored at each znode in a namespace is read and written
  298. atomically. Reads get all the data bytes associated with a znode and a
  299. write replaces all the data. Each node has an Access Control List (ACL)
  300. that restricts who can do what.</p>
  301. <p>ZooKeeper also has the notion of ephemeral nodes. These znodes
  302. exists as long as the session that created the znode is active. When the
  303. session ends the znode is deleted. Ephemeral nodes are useful when you
  304. want to implement <remark>[tbd]</remark>.</p>
  305. <a name="N10075"></a><a name="Conditional+updates+and+watches"></a>
  306. <h3 class="h4">Conditional updates and watches</h3>
  307. <p>ZooKeeper supports the concept of <em>watches</em>.
  308. Clients can set a watch on a znodes. A watch will be triggered and
  309. removed when the znode changes. When a watch is triggered the client
  310. receives a packet saying that the znode has changed. And if the
  311. connection between the client and one of the Zoo Keeper servers is
  312. broken, the client will receive a local notification. These can be used
  313. to <remark>[tbd]</remark>.</p>
  314. <a name="N10085"></a><a name="Guarantees"></a>
  315. <h3 class="h4">Guarantees</h3>
  316. <p>ZooKeeper is very fast and very simple. Since its goal, though, is
  317. to be a basis for the construction of more complicated services, such as
  318. synchronization, it provides a set of guarantees. These are:</p>
  319. <ul>
  320. <li>
  321. <p>Sequential Consistency - Updates from a client will be applied
  322. in the order that they were sent.</p>
  323. </li>
  324. <li>
  325. <p>Atomicity - Updates either succeed or fail. No partial
  326. results.</p>
  327. </li>
  328. <li>
  329. <p>Single System Image - A client will see the same view of the
  330. service regardless of the server that it connects to.</p>
  331. </li>
  332. </ul>
  333. <ul>
  334. <li>
  335. <p>Reliability - Once an update has been applied, it will persist
  336. from that time forward until a client overwrites the update.</p>
  337. </li>
  338. </ul>
  339. <ul>
  340. <li>
  341. <p>Timeliness - The clients view of the system is guaranteed to
  342. be up-to-date within a certain time bound.</p>
  343. </li>
  344. </ul>
  345. <p>For more information on these, and how they can be used, see
  346. <remark>[tbd]</remark>
  347. </p>
  348. <a name="N100BB"></a><a name="Simple+API"></a>
  349. <h3 class="h4">Simple API</h3>
  350. <p>One of the design goals of ZooKeeper is provide a very simple
  351. programming interface. As a result, it supports only these
  352. operations:</p>
  353. <dl>
  354. <dt>
  355. <term>create</term>
  356. </dt>
  357. <dd>
  358. <p>creates a node at a location in the tree</p>
  359. </dd>
  360. <dt>
  361. <term>delete</term>
  362. </dt>
  363. <dd>
  364. <p>deletes a node</p>
  365. </dd>
  366. <dt>
  367. <term>exists</term>
  368. </dt>
  369. <dd>
  370. <p>tests if a node exists at a location</p>
  371. </dd>
  372. <dt>
  373. <term>get data</term>
  374. </dt>
  375. <dd>
  376. <p>reads the data from a node</p>
  377. </dd>
  378. <dt>
  379. <term>set data</term>
  380. </dt>
  381. <dd>
  382. <p>writes data to a node</p>
  383. </dd>
  384. <dt>
  385. <term>get children</term>
  386. </dt>
  387. <dd>
  388. <p>retrieves a list of children of a node</p>
  389. </dd>
  390. <dt>
  391. <term>sync</term>
  392. </dt>
  393. <dd>
  394. <p>waits for data to be propagated</p>
  395. </dd>
  396. </dl>
  397. <p>For a more in-depth discussion on these, and how they can be used
  398. to implement higher level operations, please refer to
  399. <remark>[tbd]</remark>
  400. </p>
  401. <a name="N100FE"></a><a name="Implementation"></a>
  402. <h3 class="h4">Implementation</h3>
  403. <p>
  404. <a href="#fg_zkComponents">ZooKeeper Components</a> shows the high-level components
  405. of the ZooKeeper service. With the exception of the request processor,
  406. <remark>[tbd: where does the request processor live?]</remark> each of
  407. the servers that make up the ZooKeeper service replicates its own copy
  408. of each of components. <remark>[tbd: I changed the wording in this
  409. sentence from the white paper. Can someone please make sure it is still
  410. correct?]</remark>
  411. </p>
  412. <p>
  413. <table class="ForrestTable" cellspacing="1" cellpadding="4">
  414. <tr>
  415. <td>ZooKeeper Components</td>
  416. </tr>
  417. <tr>
  418. <td>
  419. <img alt="" src="images/zkcomponents.jpg">
  420. </td>
  421. </tr>
  422. </table>
  423. </p>
  424. <p>The replicated database is an in-memory database containing the
  425. entire data tree. Updates are logged to disk for recoverability, and
  426. writes are serialized to disk before they are applied to the in-memory
  427. database.</p>
  428. <p>Every ZooKeeper server services clients. Clients connect to
  429. exactly one server to submit irequests. Read requests are serviced from
  430. the local replica of each server database. Requests that change the
  431. state of the service, write requests, are processed by an agreement
  432. protocol.</p>
  433. <p>As part of the agreement protocol all write requests from clients
  434. are forwarded to a single server, called the
  435. <em>leader</em>. The rest of the ZooKeeper servers, called
  436. <em>followers</em>, receive message proposals from the
  437. leader and agree upon message delivery. The messaging layer takes care
  438. of replacing leaders on failures and syncing followers with
  439. leaders.</p>
  440. <p>ZooKeeper uses a custom atomic messaging protocol. Since the
  441. messaging layer is atomic, ZooKeeper can guarantee that the local
  442. replicas never diverge. When the leader receives a write request, it
  443. calculates what the state of the system is when the write is to be
  444. applied and transforms this into a transaction that captures this new
  445. state.</p>
  446. <a name="N1012F"></a><a name="Uses"></a>
  447. <h3 class="h4">Uses</h3>
  448. <p>The programming interface to ZooKeeper is deliberately simple.
  449. With it, however, you can implement higher order operations, such as
  450. synchronizations primitives, group membership, ownership, etc. Some
  451. distributed applications have used it to: <remark>[tbd: add uses from
  452. white paper and video presentation.]</remark> For more information, see
  453. <remark>[tbd]</remark>
  454. </p>
  455. <a name="N1013E"></a><a name="Performance"></a>
  456. <h3 class="h4">Performance</h3>
  457. <p>ZooKeeper is designed to be highly performant. But is it? The
  458. results of the ZooKeeper's development team at Yahoo! Research indicate
  459. that it is. (See <a href="#fg_zkPerfRW">ZooKeeper Throughput as the Read-Write Ratio Varies</a>.) It is especially high
  460. performance in applications where reads outnumber writes, since writes
  461. involve synchronizing the state of all servers. (Reads outnumbering
  462. writes is typically the case for a coordination service.)</p>
  463. <p>
  464. <table class="ForrestTable" cellspacing="1" cellpadding="4">
  465. <tr>
  466. <td>ZooKeeper Throughput as the Read-Write Ratio Varies</td>
  467. </tr>
  468. <tr>
  469. <td>
  470. <img alt="" src="images/zkperfRW.jpg">
  471. </td>
  472. </tr>
  473. </table>Benchmarks also indicate that it is reliable, too. <a href="#fg_zkPerfReliability">Reliability in the Presence of Errors</a> shows how a deployment responds to
  474. various failures. The events marked in the figure are the
  475. following:</p>
  476. <ol>
  477. <li>
  478. <p>Failure and recovery of a follower</p>
  479. </li>
  480. <li>
  481. <p>Failure and recovery of a different follower</p>
  482. </li>
  483. <li>
  484. <p>Failure of the leader</p>
  485. </li>
  486. <li>
  487. <p>Failure and recovery of two followers</p>
  488. </li>
  489. <li>
  490. <p>Failure of another leader</p>
  491. </li>
  492. </ol>
  493. <p>
  494. <table class="ForrestTable" cellspacing="1" cellpadding="4">
  495. <tr>
  496. <td>Reliability in the Presence of Errors</td>
  497. </tr>
  498. <tr>
  499. <td>
  500. <img alt="" src="images/zkperfreliability.jpg">
  501. </td>
  502. </tr>
  503. </table>
  504. </p>
  505. <p>The are a few important observations from this graph. First, if
  506. followers fail and recover quickly, then ZooKeeper is able to sustain a
  507. high throughput despite the failure. But maybe more importantly, the
  508. leader election algorithm allows for the system to recover fast enough
  509. to prevent throughput from dropping substantially. In our observations,
  510. ZooKeeper takes less than 200ms to elect a new leader. Third, as
  511. followers recover, ZooKeeper is able to raise throughput again once they
  512. start processing requests.</p>
  513. <a name="N1018F"></a><a name="The+ZooKeeper+Project"></a>
  514. <h3 class="h4">The ZooKeeper Project</h3>
  515. <p>ZooKeeper has been successfully used in industrial applications.
  516. It is used at Yahoo! as the coordination and failure recovery service
  517. for Yahoo! Message Broker, which is a highly scalable publish-subscribe
  518. system managing thousands of topics for replication and data delivery.
  519. It is used by the Fetching Service for Yahoo! crawler, where it also
  520. manages failure recovery. And it is used by Hadoop On Demand (HOD),
  521. which is an open source implementation of the map-reduce model of
  522. computation. HOD uses Zookeeper as a communications and control channel
  523. between slave and master process. (For more information, see the <a href="http://hadoop.apache.org/core/">Hadoop</a> and <a href="http://hadoop.apache.org/core/docs/current/hod.html">Hadoop on
  524. Demand</a> open source projects on Apache.)</p>
  525. <p>ZooKeeper itself is an open source project, under the Apache Open
  526. Source Foundation. It is a subproject of Hadoop. All users and
  527. developers are encourged to join the community and contribute their
  528. expertise. See the <a href="http://hadoop.apache.org/zookeeper/">Zookeeper Project on
  529. Apache</a> for more information.</p>
  530. </div>
  531. <p align="right">
  532. <font size="-2"></font>
  533. </p>
  534. </div>
  535. <!--+
  536. |end content
  537. +-->
  538. <div class="clearboth">&nbsp;</div>
  539. </div>
  540. <div id="footer">
  541. <!--+
  542. |start bottomstrip
  543. +-->
  544. <div class="lastmodified">
  545. <script type="text/javascript"><!--
  546. document.write("Last Published: " + document.lastModified);
  547. // --></script>
  548. </div>
  549. <div class="copyright">
  550. Copyright &copy;
  551. 2008 <a href="http://www.apache.org/licenses/">The Apache Software Foundation.</a>
  552. </div>
  553. <!--+
  554. |end bottomstrip
  555. +-->
  556. </div>
  557. </body>
  558. </html>