View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/event/datalist/dao/AbstractDataSource.java,v 1.7 2006/01/25 16:35:23 hkollmann Exp $
3    * $Revision: 1.7 $
4    * $Date: 2006/01/25 16:35:23 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  package org.dbforms.event.datalist.dao;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  import org.dbforms.config.DbFormsConfigRegistry;
29  import org.dbforms.config.Field;
30  import org.dbforms.config.FieldTypes;
31  import org.dbforms.config.FieldValue;
32  import org.dbforms.config.FieldValues;
33  import org.dbforms.config.ResultSetVector;
34  import org.dbforms.config.Table;
35  import org.dbforms.interfaces.DbEventInterceptorData;
36  
37  import org.dbforms.util.FileHolder;
38  import org.dbforms.util.Util;
39  
40  import java.io.File;
41  import java.io.IOException;
42  
43  import java.sql.Connection;
44  import java.sql.SQLException;
45  
46  import java.util.Iterator;
47  
48  /***
49   * Abstract base class for DataSource.
50   * 
51   * @author hkk
52   */
53  public abstract class AbstractDataSource {
54  	/*** log4j category class */
55  	private static Log logCat = LogFactory.getLog(AbstractDataSource.class);
56  
57  	private Table table;
58  
59  	/***
60  	 * Creates a new DataSource object.
61  	 */
62  	public AbstractDataSource() {
63  	}
64  
65  	/***
66  	 * Sets the select data for this dataSource
67  	 * 
68  	 * @param filterConstraint
69  	 *            FieldValue array used to restrict a set in a resultset
70  	 * @param orderConstraint
71  	 *            FieldValue array used to build a cumulation of rules for
72  	 *            ordering (sorting)
73  	 * @param sqlFilter
74  	 *            sql condition to add to where clause
75  	 * @param sqlFilterParams
76  	 *            list of FieldValues to fill the sqlFilter with
77  	 */
78  	public abstract void setSelect(FieldValue[] filterConstraint,
79  			FieldValue[] orderConstraint, String sqlFilter,
80  			FieldValue[] sqlFilterParams);
81  
82  	/***
83  	 * get count rows from position
84  	 * 
85  	 * @param position
86  	 *            keyValueStr to the row<br>
87  	 *            key format: FieldID ":" Length ":" Value<br>
88  	 *            example: if key id = 121 and field id=2 then keyValueStr
89  	 *            contains "2:3:121"<br>
90  	 *            If the key consists of more than one fields, the key values
91  	 *            are seperated through "-"<br>
92  	 *            example: value of field 1=12, value of field 3=1992, then
93  	 *            we'll get "1:2:12-3:4:1992"
94  	 * @param count
95  	 *            count of rows to fetch
96  	 * 
97  	 * @return the fetched ResultSetVector
98  	 * 
99  	 * @throws SQLException
100 	 */
101 	public ResultSetVector getCurrent(DbEventInterceptorData interceptorData,
102 			String position, int count) throws SQLException {
103 		try {
104 			open();
105 
106 			int start = findStartRow(position);
107 
108 			if (count == 0) {
109 				count = size() - start;
110 			}
111 
112 			return getResultSetVector(interceptorData, start, count);
113 		} catch (Exception e) {
114 			logCat.error("getCurrent", e);
115 			close();
116 			// Valid result is required!!!
117 			return new ResultSetVector(getTable());
118 		}
119 	}
120 
121 	/***
122 	 * get count rows from first row
123 	 * 
124 	 * @param count
125 	 *            count of rows to fetch
126 	 * 
127 	 * @return the fetched ResultSetVector
128 	 * 
129 	 * @throws SQLException
130 	 */
131 	public ResultSetVector getFirst(DbEventInterceptorData interceptorData,
132 			int count) throws SQLException {
133 		try {
134 			open();
135 
136 			if (count == 0) {
137 				count = size();
138 			}
139 
140 			return getResultSetVector(interceptorData, 0, count);
141 		} catch (Exception e) {
142 			logCat.error("getFirst", e);
143 			close();
144 
145 			// Valid result is required!!!
146 			return new ResultSetVector(getTable());
147 		}
148 	}
149 
150 	/***
151 	 * get count rows from last row
152 	 * 
153 	 * @param count
154 	 *            count of rows to fetch
155 	 * 
156 	 * @return the fetched ResultSetVector
157 	 * 
158 	 * @throws SQLException
159 	 */
160 	public ResultSetVector getLast(DbEventInterceptorData interceptorData,
161 			int count) throws SQLException {
162 		try {
163 			open();
164 
165 			return getResultSetVector(interceptorData, size() - 1, -count);
166 		} catch (Exception e) {
167 			logCat.error("getLast", e);
168 			close();
169 
170 			// Valid result is required!!!
171 			return new ResultSetVector(getTable());
172 		}
173 	}
174 
175 	/***
176 	 * get count next rows from position
177 	 * 
178 	 * @param position
179 	 *            keyValueStr to the row<br>
180 	 *            key format: FieldID ":" Length ":" Value<br>
181 	 *            example: if key id = 121 and field id=2 then keyValueStr
182 	 *            contains "2:3:121"<br>
183 	 *            If the key consists of more than one fields, the key values
184 	 *            are seperated through "-"<br>
185 	 *            example: value of field 1=12, value of field 3=1992, then
186 	 *            we'll get "1:2:12-3:4:1992"
187 	 * @param count
188 	 *            count of rows to fetch
189 	 * 
190 	 * @return the fetched ResultSetVector
191 	 * 
192 	 * @throws SQLException
193 	 */
194 	public ResultSetVector getNext(DbEventInterceptorData interceptorData,
195 			String position, int count) throws SQLException {
196 		try {
197 			open();
198 
199 			int start = findStartRow(position) + 1;
200 
201 			if (count == 0) {
202 				count = size() - start;
203 			}
204 
205 			return getResultSetVector(interceptorData, start, count);
206 		} catch (Exception e) {
207 			logCat.error("getNext", e);
208 			close();
209 
210 			// Valid result is required!!!
211 			return new ResultSetVector(getTable());
212 		}
213 	}
214 
215 	/***
216 	 * get count rows backwards from position
217 	 * 
218 	 * @param position
219 	 *            keyValueStr to the row<br>
220 	 *            key format: FieldID ":" Length ":" Value<br>
221 	 *            example: if key id = 121 and field id=2 then keyValueStr
222 	 *            contains "2:3:121"<br>
223 	 *            If the key consists of more than one fields, the key values
224 	 *            are seperated through "-"<br>
225 	 *            example: value of field 1=12, value of field 3=1992, then
226 	 *            we'll get "1:2:12-3:4:1992"
227 	 * @param count
228 	 *            count of rows to fetch
229 	 * 
230 	 * @return the fetched ResultSetVector
231 	 * 
232 	 * @throws SQLException
233 	 */
234 	public ResultSetVector getPrev(DbEventInterceptorData interceptorData,
235 			String position, int count) throws SQLException {
236 		try {
237 			open();
238 
239 			int start = findStartRow(position) - 1;
240 
241 			if (count == 0) {
242 				count = start;
243 			}
244 
245 			return getResultSetVector(interceptorData, start, -count);
246 		} catch (Exception e) {
247 			logCat.error("getPrev", e);
248 			close();
249 
250 			// Valid result is required!!!
251 			return new ResultSetVector(getTable());
252 		}
253 	}
254 
255 	/***
256 	 * Sets the select data for this dataSource for free form selects. default
257 	 * methods just raises an exception
258 	 * 
259 	 * @param tableList
260 	 *            the list of tables involved into the query
261 	 * @param whereClause
262 	 *            free-form whereClause to be appended to query
263 	 * 
264 	 * @throws SQLException
265 	 */
266 	public void setSelect(String tableList, String whereClause)
267 			throws SQLException {
268 		throw new SQLException("Free form select not implemented");
269 	}
270 
271 	/***
272 	 * gets the Table for the DataSource
273 	 * 
274 	 * @return Table
275 	 */
276 	public Table getTable() {
277 		return table;
278 	}
279 
280 	/***
281 	 * performs an delete in the DataSource
282 	 * 
283 	 * @param con
284 	 *            DOCUMENT ME!
285 	 * @param keyValuesStr
286 	 *            keyValueStr to the row to update<br>
287 	 *            key format: FieldID ":" Length ":" Value<br>
288 	 *            example: if key id = 121 and field id=2 then keyValueStr
289 	 *            contains "2:3:121"<br>
290 	 *            If the key consists of more than one fields, the key values
291 	 *            are seperated through "-"<br>
292 	 *            example: value of field 1=12, value of field 3=1992, then
293 	 *            we'll get "1:2:12-3:4:1992"
294 	 * 
295 	 * @throws SQLException
296 	 *             if any error occurs
297 	 */
298 	public int doDelete(DbEventInterceptorData interceptorData,
299 			String keyValuesStr) throws SQLException {
300 		return 0;
301 	}
302 
303 	/***
304 	 * performs an insert into the DataSource
305 	 * 
306 	 * @param con
307 	 *            DOCUMENT ME!
308 	 * @param fieldValues
309 	 *            FieldValues to insert
310 	 * 
311 	 * @throws SQLException
312 	 */
313 	public int doInsert(DbEventInterceptorData interceptorData,
314 			FieldValues fieldValues) throws SQLException {
315 		return 0;
316 	}
317 
318 	/***
319 	 * performs an update into the DataSource
320 	 * 
321 	 * @param con
322 	 *            DOCUMENT ME!
323 	 * @param fieldValues
324 	 *            FieldValues to update
325 	 * @param keyValuesStr
326 	 *            keyValueStr to the row to update<br>
327 	 *            key format: FieldID ":" Length ":" Value<br>
328 	 *            example: if key id = 121 and field id=2 then keyValueStr
329 	 *            contains "2:3:121"<br>
330 	 *            If the key consists of more than one fields, the key values
331 	 *            are seperated through "-"<br>
332 	 *            example: value of field 1=12, value of field 3=1992, then
333 	 *            we'll get "1:2:12-3:4:1992"
334 	 * 
335 	 * @throws SQLException
336 	 *             if any error occurs
337 	 */
338 	public int doUpdate(DbEventInterceptorData interceptorData,
339 			FieldValues fieldValues, String keyValuesStr) throws SQLException {
340 		return 0;
341 	}
342 
343 	/***
344 	 * set the connection parameter for the DataSouce. virtual method, if you
345 	 * need the connection data you must override this method
346 	 * 
347 	 * @param con
348 	 *            the JDBC Connection object
349 	 * @param dbConnectionName
350 	 *            name of the used db connection. Can be used to get an own db
351 	 *            connection, e.g. to hold it during the session (see
352 	 *            DataSourceJDBC for example!)
353 	 */
354 	protected void setConnection(Connection con, String dbConnectionName) {
355 	}
356 
357 	/***
358 	 * should retrieve the row at an special index as an Object[]
359 	 * 
360 	 * @param i
361 	 *            index of row to fetch
362 	 * 
363 	 * @return Object[] of the fetched row
364 	 * 
365 	 * @throws SQLException
366 	 */
367 	protected abstract Object[] getRow(int i) throws SQLException;
368 
369 	/***
370 	 * should close all open datasets
371 	 */
372 	protected abstract void close();
373 
374 	/***
375 	 * maps the startRow to the internal index
376 	 * 
377 	 * @param startRow
378 	 *            keyValueStr to the row<br>
379 	 *            key format: FieldID ":" Length ":" Value<br>
380 	 *            example: if key id = 121 and field id=2 then keyValueStr
381 	 *            contains "2:3:121"<br>
382 	 *            If the key consists of more than one fields, the key values
383 	 *            are seperated through "-"<br>
384 	 *            example: value of field 1=12, value of field 3=1992, then
385 	 *            we'll get "1:2:12-3:4:1992"
386 	 * 
387 	 * @return the index of the row, 0 as first row if not found
388 	 * 
389 	 * @throws SQLException
390 	 */
391 	protected abstract int findStartRow(String startRow) throws SQLException;
392 
393 	/***
394 	 * return true if there are more records to fetch then the given record
395 	 * number
396 	 * 
397 	 * @param i
398 	 *            index of last fetched row.
399 	 * 
400 	 * @return true if there are more records to fetch then the given record
401 	 *         number
402 	 * 
403 	 * @throws SQLException
404 	 */
405 	protected abstract boolean hasMore(int i) throws SQLException;
406 
407 	/***
408 	 * Will be called to open all datasets
409 	 * 
410 	 * @throws SQLException
411 	 */
412 	protected abstract void open() throws SQLException;
413 
414 	/***
415 	 * Must return the size of the whole resultset with all data fetch
416 	 * 
417 	 * @return size of whole resultset
418 	 * 
419 	 * @throws SQLException
420 	 */
421 	protected abstract int size() throws SQLException;
422 
423 	/***
424 	 * @param table
425 	 *            The table to set.
426 	 */
427 	protected void setTable(Table table) {
428 		this.table = table;
429 	}
430 
431 	/***
432 	 * deletes the blob files on disk
433 	 * 
434 	 * @param fieldValues
435 	 *            FieldValues to delete, called by
436 	 * 
437 	 * @throws SQLException
438 	 */
439 	protected void deleteBlobFilesFromDisk(FieldValues fieldValues)
440 			throws SQLException {
441 		Iterator iter = fieldValues.keys();
442 
443 		while (iter.hasNext()) {
444 			String fieldName = (String) iter.next();
445 			Field curField = table.getFieldByName(fieldName);
446 
447 			if (curField != null) {
448 				int fieldType = curField.getType();
449 
450 				String directory = null;
451 
452 				try {
453 					directory = DbFormsConfigRegistry.instance().lookup()
454 							.replaceVariables(curField.getDirectory());
455 				} catch (Exception e) {
456 					logCat.error("deleteBlobFilesFromDisk", e);
457 					throw new SQLException(e.getMessage());
458 				}
459 
460 				if (fieldType == FieldTypes.DISKBLOB) {
461 					String fileName = fieldValues.get(fieldName)
462 							.getFieldValue().trim();
463 
464 					// get a filename
465 					if (!Util.isNull(fileName)) {
466 						// remember: every field may have its own storing dir!
467 						File file = new File(directory, fileName);
468 
469 						if (file.exists()) {
470 							file.delete();
471 							logCat.info("deleted file " + fileName
472 									+ " from dir " + directory);
473 						} else {
474 							logCat.info("delete of file " + fileName
475 									+ " from dir " + directory
476 									+ " failed because file not found");
477 						}
478 					}
479 				}
480 			}
481 		}
482 	}
483 
484 	/***
485 	 * save the blob files to disk
486 	 * 
487 	 * @param fieldValues
488 	 *            FieldValues to update
489 	 * 
490 	 * @throws SQLException
491 	 * @throws IllegalArgumentException
492 	 */
493 	protected void saveBlobFilesToDisk(FieldValues fieldValues)
494 			throws SQLException {
495 		Iterator iter = fieldValues.keys();
496 
497 		while (iter.hasNext()) {
498 			String fieldName = (String) iter.next();
499 			Field curField = table.getFieldByName(fieldName);
500 
501 			if (curField != null) {
502 				int fieldType = curField.getType();
503 
504 				if (fieldType == FieldTypes.DISKBLOB) {
505 					String directory = curField.getDirectory();
506 
507 					// check if directory-attribute was provided
508 					if (directory == null) {
509 						throw new IllegalArgumentException(
510 								"directory-attribute needed for fields of type DISKBLOB");
511 					}
512 
513 					try {
514 						directory = DbFormsConfigRegistry.instance().lookup()
515 								.replaceVariables(directory);
516 					} catch (Exception e) {
517 						logCat.error("saveBlobFilesToDisk", e);
518 					}
519 
520 					// instanciate file object for that dir
521 					File dir = new File(directory);
522 
523 					// Check saveDirectory is truly a directory
524 					if (!dir.isDirectory()) {
525 						throw new IllegalArgumentException("Not a directory: "
526 								+ directory);
527 					}
528 
529 					// Check saveDirectory is writable
530 					if (!dir.canWrite()) {
531 						throw new IllegalArgumentException("Not writable: "
532 								+ directory);
533 					}
534 
535 					// dir is ok so lets store the filepart
536 					FileHolder fileHolder = fieldValues.get(fieldName)
537 							.getFileHolder();
538 
539 					if (fileHolder != null) {
540 						try {
541 							fileHolder.writeBufferToFile(dir);
542 
543 							// filePart.getInputStream().close();
544 							logCat.info("fin + closedy");
545 						} catch (IOException ioe) {
546 							// #checkme: this would be a good place for rollback
547 							// in database!!
548 							logCat.error("saveBlobFilesToDisk", ioe);
549 							throw new SQLException("could not store file '"
550 									+ fileHolder.getFileName() + "' to dir '"
551 									+ directory + "'");
552 						}
553 					} else {
554 						logCat.info("uh! empty fileHolder");
555 					}
556 				}
557 			}
558 		}
559 	}
560 
561 	/***
562 	 * DOCUMENT ME!
563 	 * 
564 	 * @param startRow
565 	 * @param count
566 	 * 
567 	 * @return the resultsetvector
568 	 * 
569 	 * @throws SQLException
570 	 */
571 	protected ResultSetVector getResultSetVector(
572 			DbEventInterceptorData interceptorData, int startRow, int count)
573 			throws SQLException {
574 		ResultSetVector result;
575 		int cCount = count;
576 		do {
577 			result = getResultSetVectorInternal(interceptorData, startRow,
578 					cCount);
579 			if ((count < 0) && result.isFirstPage())
580 				break;
581 			else if ((count > 0) && result.isLastPage())
582 				break;
583 			cCount = cCount + (count - result.size());
584 		} while (result.size() < Math.abs(count));
585 		return result;
586 	}
587 
588 	private ResultSetVector getResultSetVectorInternal(
589 			DbEventInterceptorData interceptorData, int startRow, int count)
590 			throws SQLException {
591 		ResultSetVector result = result = new ResultSetVector(table);
592 
593 		int begin = 0;
594 		int ende = 0;
595 
596 		Object[] row;
597 
598 		if (count > 0) {
599 			begin = startRow;
600 
601 			for (ende = begin; ende < (startRow + count); ende++) {
602 				row = getRow(ende);
603 
604 				if (row == null) {
605 					break;
606 				}
607 
608 				result.addRow(interceptorData, row);
609 			}
610 		} else if (count < 0) {
611 			begin = startRow + count + 1;
612 
613 			if (begin < 0) {
614 				begin = 0;
615 			}
616 
617 			for (ende = begin; ende <= startRow; ende++) {
618 				row = getRow(ende);
619 
620 				if (row == null) {
621 					break;
622 				}
623 
624 				result.addRow(interceptorData, row);
625 			}
626 		}
627 
628 		result.setFirstPage(!(begin > 0));
629 		result.setLastPage(!hasMore(ende));
630 
631 		return result;
632 	}
633 
634 }