1. /*
  2. * @(#)SyncResolver.java 1.4 04/02/27
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.sql.rowset.spi;
  8. import javax.sql.RowSet;
  9. import java.sql.SQLException;
  10. /**
  11. * Defines a framework that allows applications to use a manual decision tree
  12. * to decide what should be done when a synchronization conflict occurs.
  13. * Although it is not mandatory for
  14. * applications to resolve synchronization conflicts manually, this
  15. * framework provides the means to delegate to the application when conflicts
  16. * arise.
  17. * <p>
  18. * Note that a conflict is a situation where the <code>RowSet</code> object's original
  19. * values for a row do not match the values in the data source, which indicates that
  20. * the data source row has been modified since the last synchronization. Note also that
  21. * a <code>RowSet</code> object's original values are the values it had just prior to the
  22. * the last synchronization, which are not necessarily its initial values.
  23. * <p>
  24. *
  25. * <H2>Description of a <code>SyncResolver</code> Object</H2>
  26. *
  27. * A <code>SyncResolver</code> object is a specialized <code>RowSet</code> object
  28. * that implements the <code>SyncResolver</code> interface.
  29. * It <b>may</b> operate as either a connected <code>RowSet</code> object (an
  30. * implementation of the <code>JdbcRowSet</code> interface) or a connected
  31. * <code>RowSet</code> object (an implementation of the
  32. * <code>CachedRowSet</code> interface or one of its subinterfaces). For information
  33. * on the subinterfaces, see the
  34. * <a href="../package-summary.html"><code>javax.sql.rowset</code></a> package
  35. * description. The reference implementation for <code>SyncResolver</code> implements
  36. * the <code>CachedRowSet</code> interface, but other implementations
  37. * may choose to implement the <code>JdbcRowSet</code> interface to satisfy
  38. * particular needs.
  39. * <P>
  40. * After an application has attempted to synchronize a <code>RowSet</code> object with
  41. * the data source (by calling the <code>CachedRowSet</code>
  42. * method <code>acceptChanges</code>), and one or more conflicts have been found,
  43. * a rowset's <code>SyncProvider</code> object creates an instance of
  44. * <code>SyncResolver</code>. This new <code>SyncResolver</code> object has
  45. * the same number of rows and columns as the
  46. * <code>RowSet</code> object that was attempting the synchronization. The
  47. * <code>SyncResolver</code> object contains the values from the data source that caused
  48. * the conflict(s) and <code>null</code> for all other values.
  49. * In addition, it contains information about each conflict.
  50. * <P>
  51. *
  52. * <H2>Getting and Using a <code>SyncResolver</code> Object</H2>
  53. *
  54. * When the method <code>acceptChanges</code> encounters conflicts, the
  55. * <code>SyncProvider</code> object creates a <code>SyncProviderException</code>
  56. * object and sets it with the new <code>SyncResolver</code> object. The method
  57. * <code>acceptChanges</code> will throw this exception, which
  58. * the application can then catch and use to retrieve the
  59. * <code>SyncResolver</code> object it contains. The following code snippet uses the
  60. * <code>SyncProviderException</code> method <code>getSyncResolver</code> to get
  61. * the <code>SyncResolver</code> object <i>resolver</i>.
  62. * <PRE>
  63. * } catch (SyncProviderException spe) {
  64. * SyncResolver resolver = spe.getSyncResolver();
  65. * ...
  66. * }
  67. * </PRE>
  68. * <P>
  69. * With <i>resolver</i> in hand, an application can use it to get the information
  70. * it contains about the conflict or conflicts. A <code>SyncResolver</code> object
  71. * such as <i>resolver</i> keeps
  72. * track of the conflicts for each row in which there is a conflict. It also places a
  73. * lock on the table or tables affected by the rowset's command so that no more
  74. * conflicts can occur while the current conflicts are being resolved.
  75. * <P>
  76. * The following kinds of information can be obtained from a <code>SyncResolver</code>
  77. * object:
  78. * <P>
  79. * <LI>What operation was being attempted when a conflict occurred<BR>
  80. * The <code>SyncProvider</code> interface defines four constants
  81. * describing states that may occur. Three
  82. * constants describe the type of operation (update, delete, or insert) that a
  83. * <code>RowSet</code> object was attempting to perform when a conflict was discovered,
  84. * and the fourth indicates that there is no conflict.
  85. * These constants are the possible return values when a <code>SyncResolver</code> object
  86. * calls the method <code>getStatus</code>.
  87. * <PRE>
  88. * int operation = resolver.getStatus();
  89. * </PRE>
  90. * <P>
  91. * <LI>The value in the data source that caused a conflict<BR>
  92. * A conflict exists when a value that a <code>RowSet</code> object has changed
  93. * and is attempting to write to the data source
  94. * has also been changed in the data source since the last synchronization. An
  95. * application can call the <code>SyncResolver</code> method
  96. * <code>getConflictValue</code > to retrieve the
  97. * value in the data source that is the cause of the conflict because the values in a
  98. * <code>SyncResolver</code> object are the conflict values from the data source.
  99. * <PRE>
  100. * java.lang.Object conflictValue = resolver.getConflictValue(2);
  101. * </PRE>
  102. * Note that the column in <i>resolver</i> can be designated by the column number,
  103. * as is done in the preceding line of code, or by the column name.
  104. * </UL>
  105. * <P>
  106. * With the information retrieved from the methods <code>getStatus</code> and
  107. * <code>getConflictValue</code>, the application may make a determination as to
  108. * which value should be persisted in the data source. The application then calls the
  109. * <code>SyncResolver</code> method <code>setResolvedValue</code>, which sets the value
  110. * to be persisted in the <code>RowSet</code> object and also in the data source.
  111. * <PRE>
  112. * resolver.setResolvedValue("DEPT", 8390426);
  113. * </PRE>
  114. * In the preceding line of code,
  115. * the column name designates the column in the <code>RowSet</code> object
  116. * that is to be set with the given value. The column number can also be used to
  117. * designate the column.
  118. * <P>
  119. * An application calls the method <code>setResolvedValue</code> after it has
  120. * resolved all of the conflicts in the current conflict row and repeats this process
  121. * for each conflict row in the <code>SyncResolver</code> object.
  122. * <P>
  123. *
  124. * <H2>Navigating a <code>SyncResolver</code> Object</H2>
  125. *
  126. * Because a <code>SyncResolver</code> object is a <code>RowSet</code> object, an
  127. * application can use all of the <code>RowSet</code> methods for moving the cursor
  128. * to navigate a <code>SyncResolver</code> object. For example, an application can
  129. * use the <code>RowSet</code> method <code>next</code> to get to each row and then
  130. * call the <code>SyncResolver</code> method <code>getStatus</code> to see if the row
  131. * contains a conflict. In a row with one or more conflicts, the application can
  132. * iterate through the columns to find any non-null values, which will be the values
  133. * from the data source that are in conflict.
  134. * <P>
  135. * To make it easier to navigate a <code>SyncResolver</code> object, especially when
  136. * there are large numbers of rows with no conflicts, the <code>SyncResolver</code>
  137. * interface defines the methods <code>nextConflict</code> and
  138. * <code>previousConflict</code>, which move only to rows
  139. * that contain at least one conflict value. Then an application can call the
  140. * <code>SyncResolver</code> method <code>getConflictValue</code>, supplying it
  141. * with the column number, to get the conflict value itself. The code fragment in the
  142. * next section gives an example.
  143. *
  144. * <H2>Code Example</H2>
  145. *
  146. * The following code fragment demonstrates how a disconnected <code>RowSet</code>
  147. * object <i>crs</i> might attempt to synchronize itself with the
  148. * underlying data source and then resolve the conflicts. In the <code>try</code>
  149. * block, <i>crs</i> calls the method <code>acceptChanges</code>, passing it the
  150. * <code>Connection</code> object <i>con</i>. If there are no conflicts, the
  151. * changes in <i>crs</i> are simply written to the data source. However, if there
  152. * is a conflict, the method <code>acceptChanges</code> throws a
  153. * <code>SyncProviderException</code> object, and the
  154. * <code>catch</code> block takes effect. In this example, which
  155. * illustrates one of the many ways a <code>SyncResolver</code> object can be used,
  156. * the <code>SyncResolver</code> method <code>nextConflict</code> is used in a
  157. * <code>while</code> loop. The loop will end when <code>nextConflict</code> returns
  158. * <code>false</code>, which will occur when there are no more conflict rows in the
  159. * <code>SyncResolver</code> object <i>resolver</i>. In This particular code fragment,
  160. * <i>resolver</i> looks for rows that have update conflicts (rows with the status
  161. * <code>SyncResolver.UPDATE_ROW_CONFLICT</code>), and the rest of this code fragment
  162. * executes only for rows where conflicts occurred because <i>crs</i> was attempting an
  163. * update.
  164. * <P>
  165. * After the cursor for <i>resolver</i> has moved to the next conflict row that
  166. * has an update conflict, the method <code>getRow</code> indicates the number of the
  167. * current row, and
  168. * the cursor for the <code>CachedRowSet</code> object <i>crs</i> is moved to
  169. * the comparable row in <i>crs</i>. By iterating
  170. * through the columns of that row in both <i>resolver</i> and <i>crs</i>, the conflicting
  171. * values can be retrieved and compared to decide which one should be persisted. In this
  172. * code fragment, the value in <i>crs</i> is the one set as the resolved value, which means
  173. * that it will be used to overwrite the conflict value in the data source.
  174. *
  175. * <PRE>
  176. * try {
  177. *
  178. * crs.acceptChanges(con);
  179. *
  180. * } catch (SyncProviderException spe) {
  181. *
  182. * SyncResolver resolver = spe.getSyncResolver();
  183. *
  184. * Object crsValue; // value in the <code>RowSet</code> object
  185. * Object resolverValue: // value in the <code>SyncResolver</code> object
  186. * Object resolvedValue: // value to be persisted
  187. *
  188. * while(resolver.nextConflict()) {
  189. * if(resolver.getStatus() == SyncResolver.UPDATE_ROW_CONFLICT) {
  190. * int row = resolver.getRow();
  191. * crs.absolute(row);
  192. *
  193. * int colCount = crs.getMetaData().getColumnCount();
  194. * for(int j = 1; j <= colCount; j++) {
  195. * if (resolver.getConflictValue(j) != null) {
  196. * crsValue = crs.getObject(j);
  197. * resolverValue = resolver.getConflictValue(j);
  198. * . . .
  199. * // compare crsValue and resolverValue to determine
  200. * // which should be the resolved value (the value to persist)
  201. * resolvedValue = crsValue;
  202. *
  203. * resolver.setResolvedValue(j, resolvedValue);
  204. * }
  205. * }
  206. * }
  207. * }
  208. * }
  209. * </PRE>
  210. * @author Jonathan Bruce
  211. */
  212. public interface SyncResolver extends RowSet {
  213. /**
  214. * Indicates that a conflict occurred while the <code>RowSet</code> object was
  215. * attempting to update a row in the data source.
  216. * The values in the data source row to be updated differ from the
  217. * <code>RowSet</code> object's original values for that row, which means that
  218. * the row in the data source has been updated or deleted since the last
  219. * synchronization.
  220. */
  221. public static int UPDATE_ROW_CONFLICT = 0;
  222. /**
  223. * Indicates that a conflict occurred while the <code>RowSet</code> object was
  224. * attempting to delete a row in the data source.
  225. * The values in the data source row to be updated differ from the
  226. * <code>RowSet</code> object's original values for that row, which means that
  227. * the row in the data source has been updated or deleted since the last
  228. * synchronization.
  229. */
  230. public static int DELETE_ROW_CONFLICT = 1;
  231. /**
  232. * Indicates that a conflict occurred while the <code>RowSet</code> object was
  233. * attempting to insert a row into the data source. This means that a
  234. * row with the same primary key as the row to be inserted has been inserted
  235. * into the data source since the last synchronization.
  236. */
  237. public static int INSERT_ROW_CONFLICT = 2;
  238. /**
  239. * Indicates that <b>no</b> conflict occured while the <code>RowSet</code> object
  240. * was attempting to update, delete or insert a row in the data source. The values in
  241. * the <code>SyncResolver</code> will contain <code>null</code> values only as an indication
  242. * that no information in pertitent to the conflict resolution in this row.
  243. */
  244. public static int NO_ROW_CONFLICT = 3;
  245. /**
  246. * Retrieves the conflict status of the current row of this <code>SyncResolver</code>,
  247. * which indicates the operation
  248. * the <code>RowSet</code> object was attempting when the conflict occurred.
  249. *
  250. * @return one of the following constants:
  251. * <code>SyncResolver.UPDATE_ROW_CONFLICT</code>,
  252. * <code>SyncResolver.DELETE_ROW_CONFLICT</code>,
  253. * <code>SyncResolver.INSERT_ROW_CONFLICT</code>, or
  254. * <code>SyncResolver.NO_ROW_CONFLICT</code>
  255. */
  256. public int getStatus();
  257. /**
  258. * Retrieves the value in the designated column in the current row of this
  259. * <code>SyncResolver</code> object, which is the value in the data source
  260. * that caused a conflict.
  261. *
  262. * @param index an <code>int</code> designating the column in this row of this
  263. * <code>SyncResolver</code> object from which to retrieve the value
  264. * causing a conflict
  265. * @return the value of the designated column in the current row of this
  266. * <code>SyncResolver</code> object
  267. * @throws SQLException if a database access error occurs
  268. */
  269. public Object getConflictValue(int index) throws SQLException;
  270. /**
  271. * Retrieves the value in the designated column in the current row of this
  272. * <code>SyncResolver</code> object, which is the value in the data source
  273. * that caused a conflict.
  274. *
  275. * @param columnName a <code>String</code> object designating the column in this row of this
  276. * <code>SyncResolver</code> object from which to retrieve the value
  277. * causing a conflict
  278. * @return the value of the designated column in the current row of this
  279. * <code>SyncResolver</code> object
  280. * @throws SQLException if a database access error occurs
  281. */
  282. public Object getConflictValue(String columnName) throws SQLException;
  283. /**
  284. * Sets <i>obj</i> as the value in column <i>index</i> in the current row of the
  285. * <code>RowSet</code> object that is being synchronized. <i>obj</i>
  286. * is set as the value in the data source internally.
  287. *
  288. * @param index an <code>int</code> giving the number of the column into which to
  289. * set the value to be persisted
  290. * @param obj an <code>Object</code> that is the value to be set in the
  291. * <code>RowSet</code> object and persisted in the data source
  292. * @throws SQLException if a database access error occurs
  293. */
  294. public void setResolvedValue(int index, Object obj) throws SQLException;
  295. /**
  296. * Sets <i>obj</i> as the value in column <i>columnName</i> in the current row of the
  297. * <code>RowSet</code> object that is being synchronized. <i>obj</i>
  298. * is set as the value in the data source internally.
  299. *
  300. * @param columnName a <code>String</code> object giving the name of the column
  301. * into which to set the value to be persisted
  302. * @param obj an <code>Object</code> that is the value to be set in the
  303. * <code>RowSet</code> object and persisted in the data source
  304. * @throws SQLException if a database access error occurs
  305. */
  306. public void setResolvedValue(String columnName, Object obj) throws SQLException;
  307. /**
  308. * Moves the cursor down from its current position to the next row that contains
  309. * a conflict value. A <code>SyncResolver</code> object's
  310. * cursor is initially positioned before the first conflict row; the first call to the
  311. * method <code>nextConflict</code> makes the first conflict row the current row;
  312. * the second call makes the second conflict row the current row, and so on.
  313. * <p>
  314. * A call to the method <code>nextConflict</code> will implicitly close
  315. * an input stream if one is open and will clear the <code>SyncResolver</code>
  316. * object's warning chain.
  317. *
  318. * @return <code>true</code> if the new current row is valid; <code>false</code>
  319. * if there are no more rows
  320. * @throws SQLException if a database access error occurs or the result set type
  321. * is <code>TYPE_FORWARD_ONLY</code>
  322. *
  323. */
  324. public boolean nextConflict() throws SQLException;
  325. /**
  326. * Moves the cursor up from its current position to the previous conflict
  327. * row in this <code>SyncResolver</code> object.
  328. * <p>
  329. * A call to the method <code>previousConflict</code> will implicitly close
  330. * an input stream if one is open and will clear the <code>SyncResolver</code>
  331. * object's warning chain.
  332. *
  333. * @return <code>true</code> if the cursor is on a valid row; <code>false</code>
  334. * if it is off the result set
  335. * @throws SQLException if a database access error occurs or the result set type
  336. * is <code>TYPE_FORWARD_ONLY</code>
  337. */
  338. public boolean previousConflict() throws SQLException;
  339. }