1 <!DOCTYPE doctype PUBLIC "-//w3c//dtd html 4.0 transitional//en"> 2 <html> 3 <head> 4 5 <meta http-equiv="Content-Type" 6 content="text/html; charset=iso-8859-1"> 7 8 <meta name="GENERATOR" 9 content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]"> 10 <!-- 11 Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 12 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 13 14 This code is free software; you can redistribute it and/or modify it 15 under the terms of the GNU General Public License version 2 only, as 16 published by the Free Software Foundation. Oracle designates this 17 particular file as subject to the "Classpath" exception as provided 18 by Oracle in the LICENSE file that accompanied this code. 19 20 This code is distributed in the hope that it will be useful, but WITHOUT 21 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 23 version 2 for more details (a copy is included in the LICENSE file that 24 accompanied this code). 25 26 You should have received a copy of the GNU General Public License version 27 2 along with this work; if not, write to the Free Software Foundation, 28 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 29 30 Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 31 or visit www.oracle.com if you need additional information or have any 32 questions. 33 --> 34 <title>javax.sql.rowset.spi</title> 35 36 </head> 37 <body bgcolor="#ffffff"> 38 39 The standard classes and interfaces that a third party vendor has to 40 use in its implementation of a synchronization provider. These classes and 41 interfaces are referred to as the Service Provider Interface (SPI). A vendor may 42 have its implementation included on the JDBC web page that lists available 43 <code>SyncProvider</code> implementations by sending email to <code>jdbc@sun.com</code>. 44 Doing this helps make developers aware of the implementation. To make it possible 45 for a <code>RowSet</code> object to use an implementation, the vendor must register 46 it with the <code>SyncFactory</code> singleton. (See the class comment for 47 <code>SyncProvider</code> for a full explanation of the registration process and 48 the naming convention to be used.) 49 50 <h2>Table of Contents</h2> 51 <ul> 52 <li><a href="#pkgspec">1.0 Package Specification</a> 53 <li><a href="#arch">2.0 Service Provider Architecture</a> 54 <li><a href="#impl">3.0 Implementer's Guide</a> 55 <li><a href="#resolving">4.0 Resolving Synchronization Conflicts</a> 56 <li><a href="#relspec">5.0 Related Specifications</a> 57 <li><a href="#reldocs">6.0 Related Documentation</a> 58 </ul> 59 60 <h3><a name="pkgspec">1.0 Package Specification</a></h3> 61 <P> 62 The following classes and interfaces make up the <code>javax.sql.rowset.spi</code> 63 package: 64 <UL> 65 <LI><code>SyncFactory</code> 66 <LI><code>SyncProvider</code> 67 <LI><code>SyncFactoryException</code> 68 <LI><code>SyncProviderException</code> 69 <LI><code>SyncResolver</code> 70 <LI><code>XmlReader</code> 71 <LI><code>XmlWriter</code> 72 <LI><code>TransactionalWriter</code> 73 </UL> 74 The following interfaces, in the <code>javax.sql</code> package, are also part of the SPI: 75 <UL> 76 <LI><code>RowSetReader</code> 77 <LI><code>RowSetWriter</code> 78 </UL> 79 <P> 80 A <code>SyncProvider</code> implementation provides a disconnected <code>RowSet</code> 81 object with the mechanisms for reading data into it and for writing data that has been 82 modified in it 83 back to the underlying data source. A <i>reader</i>, a <code>RowSetReader</code> or 84 <code>XMLReader</code> object, reads data into a <code>RowSet</code> object when the 85 <code>CachedRowSet</code> methods <code>execute</code> or <code>populate</code> 86 are called. A <i>writer</i>, a <code>RowSetWriter</code> or <code>XMLWriter</code> 87 object, writes changes back to the underlying data source when the 88 <code>CachedRowSet</code> method <code>acceptChanges</code> is called. 89 <P> 90 The process of writing changes in a <code>RowSet</code> object to its data source 91 is known as <i>synchronization</i>. The <code>SyncProvider</code> implementation that a 92 <code>RowSet</code> object is using determines the level of synchronization that the 93 <code>RowSet</code> object's writer uses. The various levels of synchronization are 94 referred to as <i>grades</i>. 95 <P> 96 The lower grades of synchronization are 97 known as <i>optimistic</i> concurrency levels because they optimistically 98 assume that there will be no conflicts or very few conflicts. A conflict exists when 99 the same data modified in the <code>RowSet</code> object has also been modified 100 in the data source. Using the optimistic concurrency model means that if there 101 is a conflict, modifications to either the data source or the <code>RowSet</code> 102 object will be lost. 103 <P> 104 Higher grades of synchronization are called <i>pessimistic</i> because they assume 105 that others will be accessing the data source and making modifications. These 106 grades set varying levels of locks to increase the chances that no conflicts 107 occur. 108 <P> 109 The lowest level of synchronization is simply writing any changes made to the 110 <code>RowSet</code> object to its underlying data source. The writer does 111 nothing to check for conflicts. 112 If there is a conflict and the data 113 source values are overwritten, the changes other parties have made by to the data 114 source are lost. 115 <P> 116 The <code>RIXMLProvider</code> implementation uses the lowest level 117 of synchronization and just writes <code>RowSet</code> changes to the data source. 118 This is true because typically XML data sources do not enable transaction 119 techniques for maintaining the integrity of data. However, specific standards 120 groups have considered offering XML-based synchronization. For details, see 121 <PRE> 122 <a href="http://www.syncml.org">http://www.syncml.org</a> 123 </PRE> 124 <P> 125 For the the next level up, the 126 writer checks to see if there are any conflicts, and if there are, 127 it does not write anything to the data source. The problem with this concurrency 128 level is that if another party has modified the corresponding data in the data source 129 since the <code>RowSet</code> object got its data, 130 the changes made to the <code>RowSet</code> object are lost. The 131 <code>RIOptimisticProvider</code> implementation uses this level of synchronization. 132 <P> 133 At higher levels of synchronization, referred to as pessimistic concurrency, 134 the writer take steps to avoid conflicts by setting locks. Setting locks 135 can vary from setting a lock on a single row to setting a lock on a table 136 or the entire data source. The level of synchronization is therefore a tradeoff 137 between the ability of users to access the data source concurrently and the ability 138 of the writer to keep the data in the <code>RowSet</code> object and its data source 139 synchronized. 140 <P> 141 It is a requirement that all disconnected <code>RowSet</code> objects 142 (<code>CachedRowSet</code>, <code>FilteredRowSet</code>, <code>JoinRowSet</code>, 143 and <code>WebRowSet</code> objects) obtain their <code>SyncProvider</code> objects 144 from the <code>SyncFactory</code> mechanism. 145 <P> 146 The reference implementation (RI) provides two synchronization providers. 147 <UL> 148 <LI><b><tt>RIOptimisticProvider</tt></b> <br> 149 The default provider that the <code>SyncFactory</code> instance will 150 supply to a disconnected <code>RowSet</code> object when no provider 151 implementation is specified.<BR> 152 This synchronization provider uses an optimistic concurrency model, 153 assuming that there will be few conflicts among users 154 who are accessing the same data in a database. It avoids 155 using locks; rather, it checks to see if there is a conflict 156 before trying to synchronize the <code>RowSet</code> object and the 157 data source. If there is a conflict, it does nothing, meaning that 158 changes to the <code>RowSet</code> object are not persisted to the data 159 source. 160 <LI><B><tt>RIXMLProvider</tt></B> <BR> 161 A synchronization provider that can be used with a 162 <code>WebRowSet</code> object, which is a rowset that can be written 163 in XML format or read from XML format. The 164 <code>RIXMLProvider</code> implementation does no checking at all for 165 conflicts and simply writes any updated data in the 166 <code>WebRowSet</code> object to the underlying data source. 167 <code>WebRowSet</code> objects use this provider when they are 168 dealing with XML data. 169 </UL> 170 171 These <code>SyncProvider</code> implementations 172 are bundled with the reference implementation, which makes them always available to 173 <code>RowSet</code> implementations. 174 <code>SyncProvider</code> implementations make themselves available by being 175 registered with the <code>SyncFactory</code> singleton. When a <code>RowSet</code> 176 object requests a provider, by specifying it in the constructor or as an argument to the 177 <code>CachedRowSet</code> method <code>setSyncProvider</code>, 178 the <code>SyncFactory</code> singleton 179 checks to see if the requested provider has been registered with it. 180 If it has, the <code>SyncFactory</code> creates an instance of it and passes it to the 181 requesting <code>RowSet</code> object. 182 If the <code>SyncProvider</code> implementation that is specified has not been registered, 183 the <code>SyncFactory</code> singleton causes a <code>SyncFactoryException</code> object 184 to be thrown. If no provider is specified, 185 the <code>SyncFactory</code> singleton will create an instance of the default 186 provider implementation, <code>RIOptimisticProvider</code>, 187 and pass it to the requesting <code>RowSet</code> object. 188 189 <P> 190 If a <code>WebRowSet</code> object does not specify a provider in its constructor, the 191 <code>SyncFactory</code> will give it an instance of <code>RIOptimisticProvider</code>. 192 However, the constructor for <code>WebRowSet</code> is implemented to set the provider 193 to the <code>RIXMLProvider</code>, which reads and writes a <code>RowSet</code> object 194 in XML format. 195 <P> 196 See the <a href="SyncProvider.html">SyncProvider</a> class 197 specification for further details. 198 <p> 199 Vendors may develop a <tt>SyncProvider</tt> implementation with any one of the possible 200 levels of synchronization, thus giving <code>RowSet</code> objects a choice of 201 synchronization mechanisms. A vendor can make its implementation available by 202 registering the fully qualified class name with Oracle Corporation at 203 <code>jdbc@sun.com</code>. This process is discussed in further detail below. 204 205 <h3><a name="arch">2.0 Service Provider Interface Architecture</a></h3> 206 <b>2.1 Overview</b> 207 <p> 208 The Service Provider Interface provides a pluggable mechanism by which 209 <code>SyncProvider</code> implementations can be registered and then generated when 210 required. The lazy reference mechanism employed by the <code>SyncFactory</code> limits 211 unnecessary resource consumption by not creating an instance until it is 212 required by a disconnected 213 <code>RowSet</code> object. The <code>SyncFactory</code> class also provides 214 a standard API to configure logging options and streams that <b>may</b> be provided 215 by a particular <code>SyncProvider</code> implementation. 216 <p> 217 <b>2.2 Registering with the <code>SyncFactory</code></b> 218 <p> 219 A third party <code>SyncProvider</code> implementation must be registered with the 220 <code>SyncFactory</code> in order for a disconnected <code>RowSet</code> object 221 to obtain it and thereby use its <code>javax.sql.RowSetReader</code> and 222 <code>javax.sql.RowSetWriter</code> 223 implementations. The following registration mechanisms are available to all 224 <code>SyncProvider</code> implementations: 225 <ul> 226 <li><b>System properties</b> - Properties set at the command line. These 227 properties are set at run time and apply system-wide per invocation of the Java 228 application. See the section <a href="#reldocs">"Related Documentation"</a> 229 further related information. 230 231 <li><b>Property Files</b> - Properties specified in a standard property file. 232 This can be specified using a System Property or by modifying a standard 233 property file located in the platform run-time. The 234 reference implementation of this technology includes a standard property 235 file than can be edited to add additional <code>SyncProvider</code> objects. 236 237 <li><b>JNDI Context</b> - Available providers can be registered on a JNDI 238 context. The <tt>SyncFactory</tt> will attempt to load <tt>SyncProvider</tt> 239 objects bound to the context and register them with the factory. This 240 context must be supplied to the <code>SyncFactory</code> for the mechanism to 241 function correctly. 242 </ul> 243 <p> 244 Details on how to specify the system properties or properties in a property file 245 and how to configure the JNDI Context are explained in detail in the 246 <a href="SyncFactory.html"><code>SyncFactory</code></a> class description. 247 <p> 248 <b>2.3 SyncFactory Provider Instance Generation Policies</b> 249 <p> 250 The <code>SyncFactory</code> generates a requested <code>SyncProvider</code> 251 object if the provider has been correctly registered. The 252 following policies are adhered to when either a disconnected <code>RowSet</code> object 253 is instantiated with a specified <code>SyncProvider</code> implementation or is 254 reconfigured at runtime with an alternative <code>SyncProvider</code> object. 255 <ul> 256 <li> If a <code>SyncProvider</code> object is specified and the <code>SyncFactory</code> 257 contains <i>no</i> reference to the provider, a <code>SyncFactoryException</code> is 258 thrown. 259 260 <li> If a <code>SyncProvider</code> object is specified and the <code>SyncFactory</code> 261 contains a reference to the provider, the requested provider is supplied. 262 263 <li> If no <code>SyncProvider</code> object is specified, the reference 264 implementation provider <code>RIOptimisticProvider</code> is supplied. 265 </ul> 266 <p> 267 These policies are explored in more detail in the <a href="SyncFactory.html"> 268 <code>SyncFactory</code></a> class. 269 270 <h3><a name="impl">3.0 SyncProvider Implementer's Guide</a></h3> 271 272 <b>3.1 Requirements</b> 273 <p> 274 A compliant <code>SyncProvider</code> implementation that is fully pluggable 275 into the <code>SyncFactory</code> <b>must</b> extend and implement all 276 abstract methods in the <a href="SyncProvider.html"><code>SyncProvider</code></a> 277 class. In addition, an implementation <b>must</b> determine the 278 grade, locking and updatable view capabilities defined in the 279 <code>SyncProvider</code> class definition. One or more of the 280 <code>SyncProvider</code> description criteria <b>must</b> be supported. It 281 is expected that vendor implementations will offer a range of grade, locking, and 282 updatable view capabilities. 283 <p> 284 Furthermore, the <code>SyncProvider</code> naming convention <b>must</b> be followed as 285 detailed in the <a href="SyncProvider.html"><code>SyncProvider</code></a> class 286 description. 287 <p> 288 <b>3.2 Grades</b> 289 <p> 290 JSR 114 defines a set of grades to describe the quality of synchronization 291 a <code>SyncProvider</code> object can offer a disconnected <code>RowSet</code> 292 object. These grades are listed from the lowest quality of service to the highest. 293 <ul> 294 <li><b>GRADE_NONE</b> - No synchronization with the originating data source is 295 provided. A <code>SyncProvider</code> implementation returning this grade will simply 296 attempt to write any data that has changed in the <code>RowSet</code> object to the 297 underlying data source, overwriting whatever is there. No attempt is made to compare 298 original values with current values to see if there is a conflict. The 299 <code>RIXMLProvider</code> is implemented with this grade. 300 301 <li><b>GRADE_CHECK_MODIFIED_AT_COMMIT</b> - A low grade of optimistic synchronization. 302 A <code>SyncProvider</code> implementation returning this grade 303 will check for conflicts in rows that have changed between the last synchronization 304 and the current synchronization under way. Any changes in the originating data source 305 that have been modified will not be reflected in the disconnected <code>RowSet</code> 306 object. If there are no conflicts, changes in the <code>RowSet</code> object will be 307 written to the data source. If there are conflicts, no changes are written. 308 The <code>RIOptimisticProvider</code> implementation uses this grade. 309 310 <li><b>GRADE_CHECK_ALL_AT_COMMIT</b> - A high grade of optimistic synchronization. 311 A <code>SyncProvider</code> implementation returning this grade 312 will check all rows, including rows that have not changed in the disconnected 313 <code>RowSet</code> object. In this way, any changes to rows in the underlying 314 data source will be reflected in the disconnected <code>RowSet</code> object 315 when the synchronization finishes successfully. 316 317 <li><b>GRADE_LOCK_WHEN_MODIFIED</b> - A pessimistic grade of synchronization. 318 <code>SyncProvider</code> implementations returning this grade will lock 319 the row in the originating data source that corresponds to the row being changed 320 in the <code>RowSet</code> object to reduce the possibility of other 321 processes modifying the same data in the data source. 322 323 <li><b>GRADE_LOCK_WHEN_LOADED</b> - A higher pessimistic synchronization grade. 324 A <code>SyncProvider</code> implementation returning this grade will lock 325 the entire view and/or table affected by the original query used to 326 populate a <code>RowSet</code> object. 327 </ul> 328 <p> 329 <b>3.3 Locks</b> 330 <p> 331 JSR 114 defines a set of constants that specify whether any locks have been 332 placed on a <code>RowSet</code> object's underlying data source and, if so, 333 on which constructs the locks are placed. These locks will remain on the data 334 source while the <code>RowSet</code> object is disconnected from the data source. 335 <P> 336 These constants <b>should</b> be considered complementary to the 337 grade constants. The default setting for the majority of grade settings requires 338 that no data source locks remain when a <code>RowSet</code> object is disconnected 339 from its data source. 340 The grades <code>GRADE_LOCK_WHEN_MODIFIED</code> and 341 <code>GRADE_LOCK_WHEN_LOADED</code> allow a disconnected <code>RowSet</code> object 342 to have a fine-grained control over the degree of locking. 343 <ul> 344 <li><b>DATASOURCE_NO_LOCK</b> - No locks remain on the originating data source. 345 This is the default lock setting for all <code>SyncProvider</code> implementations 346 unless otherwise directed by a <code>RowSet</code> object. 347 348 <li><b>DATASOURCE_ROW_LOCK</b> - A lock is placed on the rows that are touched by 349 the original SQL query used to populate the <code>RowSet</code> object. 350 351 <li><b>DATASOURCE_TABLE_LOCK</b> - A lock is placed on all tables that are touched 352 by the query that was used to populate the <code>RowSet</code> object. 353 354 <li><b>DATASOURCE_DB_LOCK</b> 355 A lock is placed on the entire data source that is used by the <code>RowSet</code> 356 object. 357 </ul> 358 <p> 359 <b>3.4 Updatable Views</b> 360 <p> 361 A <code>RowSet</code> object may be populated with data from an SQL <code>VIEW</code>. 362 The following constants indicate whether a <code>SyncProvider</code> object can 363 update data in the table or tables from which the <code>VIEW</code> was derived. 364 <ul> 365 <li><b>UPDATABLE_VIEW_SYNC</b> 366 Indicates that a <code>SyncProvider</code> implementation supports synchronization 367 to the table or tables from which the SQL <code>VIEW</code> used to populate a 368 a <code>RowSet</code> object is derived. 369 370 <li><b>NONUPDATABLE_VIEW_SYNC</b> 371 Indicates that a <code>SyncProvider</code> implementation does <b>not</b> support 372 synchronization to the table or tables from which the SQL <code>VIEW</code> 373 used to populate a <code>RowSet</code> object is derived. 374 </ul> 375 <p> 376 <b>3.5 Usage of <code>SyncProvider</code> Grading and Locking</b> 377 <p> 378 In the example below, the reference <tt>CachedRowSetImpl</tt> implementation 379 reconfigures its current <tt>SyncProvider</tt> object by calling the 380 <tt>setSyncProvider</tt> method.<br> 381 382 <PRE> 383 CachedRowSetImpl crs = new CachedRowSetImpl(); 384 crs.setSyncProvider("com.foo.bar.HASyncProvider"); 385 </PRE> 386 An application can retrieve the <tt>SyncProvider</tt> object currently in use 387 by a disconnected <code>RowSet</code> object. It can also retrieve the 388 grade of synchronization with which the provider was implemented and the degree of 389 locking currently in use. In addition, an application has the flexibility to set 390 the degree of locking to be used, which can increase the possibilities for successful 391 synchronization. These operation are shown in the following code fragment. 392 <PRE> 393 SyncProvider sync = crs.getSyncProvider(); 394 395 switch (sync.getProviderGrade()) { 396 case: SyncProvider.GRADE_CHECK_ALL_AT_COMMIT 397 //A high grade of optimistic synchronization 398 break; 399 case: SyncProvider.GRADE_CHECK_MODIFIED_AT_COMMIT 400 //A low grade of optimistic synchronization 401 break; 402 case: SyncProvider.GRADE_LOCK_WHEN_LOADED 403 // A pessimistic synchronization grade 404 break; 405 case: SyncProvider.GRADE_LOCK_WHEN_MODIFIED 406 // A pessimistic synchronization grade 407 break; 408 case: SyncProvider.GRADE_NONE 409 // No synchronization with the originating data source provided 410 break; 411 } 412 413 switch (sync.getDataSourcLock() { 414 case: SyncProvider.DATASOURCE_DB_LOCK 415 // A lock is placed on the entire datasource that is used by the 416 // <code>RowSet</code> object 417 break; 418 419 case: SyncProvider.DATASOURCE_NO_LOCK 420 // No locks remain on the originating data source. 421 break; 422 423 case: SyncProvider.DATASOURCE_ROW_LOCK 424 // A lock is placed on the rows that are touched by the original 425 // SQL statement used to populate 426 // the RowSet object that is using the SyncProvider 427 break; 428 429 case: DATASOURCE_TABLE_LOCK 430 // A lock is placed on all tables that are touched by the original 431 // SQL statement used to populated 432 // the RowSet object that is using the SyncProvider 433 break; 434 435 </PRE> 436 It is also possible using the static utility method in the 437 <code>SyncFactory</code> class to determine the list of <code>SyncProvider</code> 438 implementations currently registered with the <code>SyncFactory</code>. 439 440 <pre> 441 Enumeration e = SyncFactory.getRegisteredProviders(); 442 </pre> 443 444 445 <h3><a name="resolving">4.0 Resolving Synchronization Conflicts</a></h3> 446 447 The interface <code>SyncResolver</code> provides a way for an application to 448 decide manually what to do when a conflict occurs. When the <code>CachedRowSet</code> 449 method <code>acceptChanges</code> finishes and has detected one or more conflicts, 450 it throws a <code>SyncProviderException</code> object. An application can 451 catch the exception and 452 have it retrieve a <code>SyncResolver</code> object by calling the method 453 <code>SyncProviderException.getSyncResolver()</code>. 454 <P> 455 A <code>SyncResolver</code> object, which is a special kind of 456 <code>CachedRowSet</code> object or 457 a <code>JdbcRowSet</code> object that has implemented the <code>SyncResolver</code> 458 interface, examines the conflicts row by row. It is a duplicate of the 459 <code>RowSet</code> object being synchronized except that it contains only the data 460 from the data source this is causing a conflict. All of the other column values are 461 set to <code>null</code>. To navigate from one conflict value to another, a 462 <code>SyncResolver</code> object provides the methods <code>nextConflict</code> and 463 <code>previousConflict</code>. 464 <P> 465 The <code>SyncResolver</code> interface also 466 provides methods for doing the following: 467 <UL> 468 <LI>finding out whether the conflict involved an update, a delete, or an insert 469 <LI>getting the value in the data source that caused the conflict 470 <LI>setting the value that should be in the data source if it needs to be changed 471 or setting the value that should be in the <code>RowSet</code> object if it needs 472 to be changed 473 </UL> 474 <P> 475 When the <code>CachedRowSet</code> method <code>acceptChanges</code> is called, it 476 delegates to the <code>RowSet</code> object's <code>SyncProvider</code> object. 477 How the writer provided by that <code>SyncProvider</code> object is implemented 478 determines what level (grade) of checking for conflicts will be done. After all 479 checking for conflicts is completed and one or more conflicts has been found, the method 480 <code>acceptChanges</code> throws a <code>SyncProviderException</code> object. The 481 application can catch the exception and use it to obtain a <code>SyncResolver</code> object. 482 <P> 483 The application can then use <code>SyncResolver</code> methods to get information 484 about each conflict and decide what to do. If the application logic or the user 485 decides that a value in the <code>RowSet</code> object should be the one to 486 persist, the application or user can overwrite the data source value with it. 487 <P> 488 The comment for the <code>SyncResolver</code> interface has more detail. 489 490 <h3><a name="relspec">5.0 Related Specifications</a></h3> 491 <ul> 492 <li><a href="http://docs.oracle.com/javase/jndi/tutorial/index.html">JNDI</a> 493 <li><a href="{@docRoot}/../technotes/guides/logging/index.html">Java Logging 494 APIs</a> 495 </ul> 496 <h3><a name="reldocs">6.0 Related Documentation</a></h3> 497 <ul> 498 <li><a href="{@docRoot}/../technotes/tools/index.html#basic">System 499 properties</a> 500 <li>Resource Files 501 <li><a href="http://docs.oracle.com/javase/tutorial/jdbc/">DataSource for JDBC 502 Connections</a> 503 </ul> 504 505 </body> 506 </html>