Source code: com/clra/web/BoatSet.java
1 /*
2 * Copyright (c) Carnegie Lake Rowing Association 2002. All rights reserved.
3 * Distributed under the GPL license. See doc/COPYING.
4 * $RCSfile: BoatSet.java,v $
5 * $Date: 2003/02/26 03:38:46 $
6 * $Revision: 1.4 $
7 */
8
9 package com.clra.web;
10
11 import com.clra.rowing.BoatView;
12 import com.clra.util.DBConfiguration;
13 import com.clra.util.ValidationException;
14 import java.io.InputStream;
15 import java.net.URL;
16 import java.sql.Connection;
17 import java.sql.Driver;
18 import java.sql.DriverManager;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.util.Collection;
23 import java.util.Comparator;
24 import java.util.GregorianCalendar;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Properties;
29 import java.util.SortedSet;
30 import java.util.TreeSet;
31 import org.apache.log4j.Category;
32 import org.apache.log4j.helpers.Loader;
33
34 /**
35 * A collection of "finder" methods that return read-only, sorted sets
36 * of boats.<p>
37 * FIXME: when this class changes to a "Session-like" design, make sure
38 * it becomes Serializable.
39 *
40 * @version $Id: BoatSet.java,v 1.4 2003/02/26 03:38:46 rphall Exp $
41 * @author <a href="mailto:rphall@pluto.njcc.com">Rick Hall</a>
42 */
43 public class BoatSet implements SortedSet {
44
45 private final static String base = BoatSet.class.getName();
46 private final static Category theLog = Category.getInstance( base );
47
48 /**
49 * The current implementations return data in an in-memory collections.
50 * Since the underlaying Boat database table may already be
51 * cached in memory, this can lead to duplicate caching. Future
52 * implementations should avoid in-memory duplicates by implementing
53 * SortedSet and Iterator methods directly in SQL.<p>
54 */
55 private final SortedSet data = new TreeSet();
56
57 /**
58 * Finds all active boats of the CLRA. Boats are sorted by name
59 * (the natural comparator for boats).
60 */
61 public static BoatSet findAllActiveBoats()
62 throws WebException {
63
64 Connection conn = null;
65 PreparedStatement stmt = null;
66 BoatSet retVal = new BoatSet();
67 try {
68 conn = DBConfiguration.getConnection();
69 stmt = conn.prepareStatement(
70 Configuration.SQL_BOAT_01,
71 ResultSet.TYPE_FORWARD_ONLY,
72 ResultSet.CONCUR_READ_ONLY);
73 loadBoatSet( stmt, retVal.data );
74 }
75 catch(SQLException x) {
76 String msg = "SQLException: " + x.getMessage();
77 theLog.fatal( msg, x );
78 throw new WebException( msg );
79 }
80 finally {
81 DBConfiguration.closeSQLStatement( stmt );
82 DBConfiguration.closeSQLConnection( conn );
83 }
84 conn = null;
85 stmt = null;
86
87 return retVal;
88 } // findAllActiveBoats()
89
90 /**
91 * Finds the boat that has the given id. This "finder" returns at most
92 * one boat.
93 */
94 public static BoatView findBoatById( int id )
95 throws WebException {
96
97 Connection conn = null;
98 PreparedStatement stmt = null;
99 BoatView retVal = null;
100 try {
101 conn = DBConfiguration.getConnection();
102 stmt = conn.prepareStatement(
103 Configuration.SQL_BOAT_02,
104 ResultSet.TYPE_FORWARD_ONLY,
105 ResultSet.CONCUR_READ_ONLY);
106 stmt.setInt( 1, id );
107 retVal = loadBoat( stmt );
108 }
109 catch(SQLException x) {
110 String msg = "SQLException for id == '" + id + "': "
111 + x.getMessage();
112 theLog.fatal( msg, x );
113 throw new WebException( msg );
114 }
115 finally {
116 DBConfiguration.closeSQLStatement( stmt );
117 DBConfiguration.closeSQLConnection( conn );
118 }
119 conn = null;
120 stmt = null;
121
122 return retVal;
123 } // findBoatById(int)
124
125 /**
126 * Finds the boat that has the given name. Names
127 * are unique, so this "finder" returns at most one boat.
128 */
129 public static BoatView findBoatByName( String name )
130 throws WebException {
131
132 Connection conn = null;
133 PreparedStatement stmt = null;
134 BoatView retVal = null;
135 try {
136 conn = DBConfiguration.getConnection();
137 stmt = conn.prepareStatement(
138 Configuration.SQL_BOAT_03,
139 ResultSet.TYPE_FORWARD_ONLY,
140 ResultSet.CONCUR_READ_ONLY);
141 stmt.setString( 1, name );
142 retVal = loadBoat( stmt );
143 }
144 catch(SQLException x) {
145 String msg = "SQLException for name == '" + name + "': "
146 + x.getMessage();
147 theLog.fatal( msg, x );
148 throw new WebException( msg );
149 }
150 finally {
151 DBConfiguration.closeSQLStatement( stmt );
152 DBConfiguration.closeSQLConnection( conn );
153 }
154 conn = null;
155 stmt = null;
156
157 return retVal;
158 } // findBoatByName(String)
159
160 /** BoatSet instances are created by static "finder" methods */
161 private BoatSet() {}
162
163 /**
164 * Returns the comparator associated with this sorted set, or
165 * <tt>null</tt> if it uses its elements' natural ordering.
166 *
167 * @return the comparator associated with this sorted set, or
168 * <tt>null</tt> if it uses its elements' natural ordering.
169 */
170 public Comparator comparator() {
171 return this.data.comparator();
172 }
173
174 /**
175 * Returns a view of the portion of this sorted set whose elements range
176 * from <tt>fromElement</tt>, inclusive, to <tt>toElement</tt>, exclusive.
177 *
178 * @param fromElement low endpoint (inclusive) of the subSet.
179 * @param toElement high endpoint (exclusive) of the subSet.
180 * @return a view of the specified range within this sorted set.
181 *
182 * @throws ClassCastException if <tt>fromElement</tt> and
183 * <tt>toElement</tt> are not <tt>Boat</tt> objects.
184 * @throws IllegalArgumentException if <tt>fromElement</tt> is greater than
185 * <tt>toElement</tt>; or if this set is itself a subSet, headSet,
186 * or tailSet, and <tt>fromElement</tt> or <tt>toElement</tt> are
187 * not within the specified range of the subSet, headSet, or
188 * tailSet.
189 * @throws NullPointerException if <tt>fromElement</tt> or
190 * <tt>toElement</tt> is <tt>null</tt>
191 */
192 public SortedSet subSet(Object fromElement, Object toElement) {
193 return this.data.subSet(fromElement,toElement);
194 }
195
196 /**
197 * Returns a view of the portion of this sorted set whose elements are
198 * strictly less than <tt>toElement</tt>.
199 *
200 * @param toElement high endpoint (exclusive) of the headSet.
201 * @return a view of the specified initial range of this sorted set.
202 * @throws ClassCastException if <tt>toElement</tt> is not compatible
203 * with this set's comparator (or, if the set has no comparator,
204 * if <tt>toElement</tt> does not implement <tt>Comparable</tt>).
205 * @throws NullPointerException if <tt>toElement</tt> is <tt>null</tt>
206 * @throws IllegalArgumentException if this set is itself a subSet,
207 * headSet, or tailSet, and <tt>toElement</tt> is not within the
208 * specified range of the subSet, headSet, or tailSet.
209 */
210 public SortedSet headSet(Object toElement) {
211 return this.data.headSet(toElement);
212 }
213
214 /**
215 * Returns a view of the portion of this sorted set whose elements are
216 * greater than or equal to <tt>fromElement</tt>.
217 *
218 * @param fromElement low endpoint (inclusive) of the tailSet.
219 * @return a view of the specified final range of this sorted set.
220 * @throws ClassCastException if <tt>fromElement</tt> is not compatible
221 * with this set's comparator (or, if the set has no comparator,
222 * if <tt>fromElement</tt> does not implement <tt>Comparable</tt>).
223 * @throws NullPointerException if <tt>fromElement</tt> is <tt>null</tt>
224 * @throws IllegalArgumentException if this set is itself a subSet,
225 * headSet, or tailSet, and <tt>fromElement</tt> is not within the
226 * specified range of the subSet, headSet, or tailSet.
227 */
228 public SortedSet tailSet(Object fromElement) {
229 return this.data.tailSet(fromElement);
230 }
231
232 /**
233 * Returns the first (lowest) element currently in this sorted set.
234 *
235 * @return the first (lowest) element currently in this sorted set.
236 * @throws NoSuchElementException sorted set is empty.
237 */
238 public Object first() {
239 return this.data.first();
240 }
241
242 /**
243 * Returns the last (highest) element currently in this sorted set.
244 *
245 * @return the last (highest) element currently in this sorted set.
246 * @throws NoSuchElementException sorted set is empty.
247 */
248 public Object last() {
249 return this.data.last();
250 }
251
252 /**
253 * Returns the number of elements in this set (its cardinality). If this
254 * set contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
255 * <tt>Integer.MAX_VALUE</tt>.
256 *
257 * @return the number of elements in this set (its cardinality).
258 */
259 public int size() {
260 return this.data.size();
261 }
262
263 /**
264 * Returns <tt>true</tt> if this set contains no elements.
265 *
266 * @return <tt>true</tt> if this set contains no elements.
267 */
268 public boolean isEmpty() {
269 return this.data.isEmpty();
270 }
271
272 /**
273 * Returns <tt>true</tt> if this set contains the specified element.
274 *
275 * @param o element whose presence in this set is to be tested.
276 * @return <tt>true</tt> if this set contains the specified element.
277 */
278 public boolean contains(Object o) {
279 /*
280 * formally, returns <tt>true</tt> if and only if this set contains an
281 * element <code>e</code> such that <code>(o==null ? e==null :
282 * o.equals(e))</code>.
283 */
284 return this.data.contains(o);
285 }
286
287 /**
288 * Returns an iterator over the (sorted) elements in this set.
289 *
290 * @return an iterator over the elements in this set.
291 */
292 public Iterator iterator() {
293 return this.data.iterator();
294 }
295
296 /**
297 * Returns an array containing all of the elements in this set.
298 *
299 * @return an array containing all of the elements in this set.
300 */
301 public Object[] toArray() {
302 return this.data.toArray();
303 }
304
305 /**
306 * Returns an array containing all of the elements in this set whose
307 * runtime type is that of the specified array.
308 *
309 * @param a the array into which the elements of this set are to
310 * be stored, if it is big enough; otherwise, a new array of the
311 * same runtime type is allocated for this purpose.
312 * @return an array containing the elements of this set.
313 * @throws ArrayStoreException the runtime type of a is not a supertype
314 * of the runtime type of Boat.
315 */
316 public Object[] toArray(Object a[]) {
317 return this.data.toArray(a);
318 }
319
320 // Bulk Operations
321
322 /**
323 * Returns <tt>true</tt> if this set contains all of the elements of the
324 * specified collection.
325 *
326 * @param c collection to be checked for containment in this set.
327 * @return <tt>true</tt> if this set contains all of the elements of the
328 * specified collection.
329 */
330 public boolean containsAll(Collection c) {
331 return this.data.containsAll(c);
332 }
333
334 // Comparison and hashing
335
336 /**
337 * Compares the specified object with this set for equality.
338 *
339 * @param o Object to be compared for equality with this set.
340 * @return <tt>true</tt> if the specified Object is equal to this set.
341 */
342 public boolean equals(Object o) {
343 return this.data.equals(o);
344 }
345
346 /**
347 * Returns the hash code value for this set. The hash code of a set is
348 * defined to be the sum of the hash codes of the elements in the set.
349 *
350 * @return the hash code value for this set.
351 */
352 public int hashCode() {
353 return this.data.hashCode();
354 }
355
356 // Modification Operations are not supported
357
358 /**
359 * @throws UnsupportedOperationException since the <tt>add</tt> method is
360 * not supported by this set.
361 */
362 public boolean add(Object o) {
363 throw new UnsupportedOperationException( "not supported" );
364 }
365
366 /**
367 * @throws UnsupportedOperationException since the <tt>remove</tt> method is
368 * not supported by this set.
369 */
370 public boolean remove(Object o) {
371 throw new UnsupportedOperationException( "not supported" );
372 }
373
374 /**
375 * @throws UnsupportedOperationException since the <tt>addAll</tt> method is
376 * not supported by this set.
377 */
378 public boolean addAll(Collection c) {
379 throw new UnsupportedOperationException( "not supported" );
380 }
381
382 /**
383 * @throws UnsupportedOperationException since the <tt>retainAll</tt> method
384 * is not supported by this Collection.
385 */
386 public boolean retainAll(Collection c) {
387 throw new UnsupportedOperationException( "not supported" );
388 }
389
390 /**
391 * @throws UnsupportedOperationException since the <tt>removeAll</tt>
392 * method is not supported by this Collection.
393 */
394 public boolean removeAll(Collection c) {
395 throw new UnsupportedOperationException( "not supported" );
396 }
397
398 /**
399 * @throws UnsupportedOperationException since the <tt>clear</tt> method
400 * is not supported by this set.
401 */
402 public void clear() {
403 throw new UnsupportedOperationException( "not supported" );
404 }
405
406 // UTILITIES
407
408 /** Loads an in-memory object from a database using the specified SQL */
409 private static BoatView loadBoat( PreparedStatement stmt )
410 throws SQLException, WebException {
411
412 // Initialize result set and return value to facilitate error recovery
413 ResultSet rs = null;
414 BoatView retVal = null;
415
416 try {
417 rs = stmt.executeQuery();
418 if ( rs.next() ) {
419 retVal = mapRowToBoat(rs);
420 } // if
421 }
422 finally {
423 if (rs != null) {
424 try {
425 rs.close();
426 rs = null;
427 }
428 catch(Exception x) {
429 theLog.error(x.getMessage(),x);
430 }
431 }
432 } // finally
433
434 return retVal;
435 } // loadBoat(PreparedStatement)
436
437 /** Loads an in-memory set from a database using the specified SQL */
438 private static void loadBoatSet( PreparedStatement stmt, SortedSet set )
439 throws SQLException, WebException {
440
441 // Initialize result set to facilitate error recovery
442 ResultSet rs = null;
443 int rowIdx = -1;
444
445 try {
446 rs = stmt.executeQuery();
447 while ( rs.next() ) {
448 ++rowIdx;
449 BoatView boat = mapRowToBoat(rs);
450 set.add( boat );
451 } // while
452 }
453 finally {
454 if (rs != null) {
455 try {
456 rs.close();
457 rs = null;
458 }
459 catch(Exception x) {
460 theLog.error(x.getMessage(),x);
461 }
462 }
463 } // finally
464 } // loadBoatSet(PreparedStatement,SortedSet)
465
466 /** Maps a row of SQL ResultSet to a BoatView object */
467 private static BoatView mapRowToBoat( ResultSet rs )
468 throws SQLException, WebException {
469
470 final int boat_id = rs.getInt( "boat_id" );
471 final String boat_name = rs.getString( "boat_name" );
472 final int boat_size = rs.getInt( "boat_size" );
473 final String boat_type = rs.getString( "boat_type" );
474
475 if ( theLog.isDebugEnabled() ) {
476 theLog.debug( boat_id + ", " + boat_name );
477 }
478
479 BoatView retVal = null;
480 try {
481 retVal = new BoatView( boat_id, boat_name, boat_size, boat_type );
482 }
483 catch( ValidationException x ) {
484 String msg = "Problem with data for boat == '" + boat_id + "', '"
485 + boat_name + "': " + x.getMessage();
486 theLog.fatal( msg, x );
487 throw new WebException( msg );
488 }
489
490 return retVal;
491 } // mapRowToBoat(ResultSet)
492
493 } // BoatSet
494
495 /*
496 * $Log: BoatSet.java,v $
497 * Revision 1.4 2003/02/26 03:38:46 rphall
498 * Added copyright and GPL license
499 *
500 * Revision 1.3 2003/02/19 22:30:31 rphall
501 * Removed gratuitous use of CLRA acronym
502 *
503 * Revision 1.2 2002/02/18 18:05:37 rphall
504 * Ran dos2unix to remove ^M (carriage return) from end of lines
505 */
506