1 /*
2 * $Source$
3 * $Revision$
4 *
5 * Copyright (C) 2000 William Chesters
6 *
7 * Part of Melati (http://melati.org), a framework for the rapid
8 * development of clean, maintainable web applications.
9 *
10 * Melati is free software; Permission is granted to copy, distribute
11 * and/or modify this software under the terms either:
12 *
13 * a) the GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the License, or (at your option)
15 * any later version,
16 *
17 * or
18 *
19 * b) any version of the Melati Software License, as published
20 * at http://melati.org
21 *
22 * You should have received a copy of the GNU General Public License and
23 * the Melati Software License along with this program;
24 * if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26 * GNU General Public License and visit http://melati.org to obtain the
27 * Melati Software License.
28 *
29 * Feel free to contact the Developers of Melati (http://melati.org),
30 * if you would like to work out a different arrangement than the options
31 * outlined here. It is our intention to allow Melati to be used by as
32 * wide an audience as possible.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * Contact details for copyright holder:
40 *
41 * William Chesters <williamc At paneris.org>
42 * http://paneris.org/~williamc
43 * Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44 */
45
46 package org.melati.poem;
47
48 import java.sql.ResultSet;
49 import java.sql.SQLException;
50 import java.util.Vector;
51
52 /**
53 * Object to return the results of a query with caching.
54 * <p>
55 * Cached results will be returned unless the relevant tables
56 * have been updated since the query was last executed, in which case
57 * the results will be recomputed.
58 *
59 */
60 public abstract class CachedQuery<T> {
61
62 protected PreparedStatementFactory statements = null;
63
64 protected Vector<Integer> rows = null;
65
66 private long tableSerial;
67
68 protected Table<?> table;
69
70 private String query;
71
72 private Table<?> otherTables[];
73
74 private long otherTablesSerial[];
75
76 /**
77 * Constructor.
78 *
79 * @param table the table to select from
80 * @param query the SQL query string
81 * @param otherTables an array of other tables involved in the query
82 */
83 public CachedQuery(final Table<?> table,
84 final String query,
85 final Table<?> otherTables[]) {
86 this.table = table;
87 this.query = query;
88 this.otherTables = otherTables;
89 if (otherTables != null)
90 otherTablesSerial = new long[otherTables.length];
91 }
92
93 protected PreparedStatementFactory statements() {
94 if (statements == null)
95 statements = new PreparedStatementFactory(
96 table.getDatabase(),
97 query);
98
99 return statements;
100 }
101
102 protected Integer extract(ResultSet rs) throws SQLException {
103 return new Integer(rs.getInt(1));
104 }
105
106 protected void compute() {
107 Vector<Integer> rowsLocal = this.rows;
108 SessionToken token = PoemThread.sessionToken();
109 if (rowsLocal == null || somethingHasChanged(token.transaction)) {
110 rowsLocal = new Vector<Integer>();
111 try {
112 ResultSet rs = statements().resultSet(token);
113 try {
114 while (rs.next())
115 rowsLocal.addElement(extract(rs));
116 } finally {
117 try {
118 rs.close();
119 } catch (Exception e) {
120 // Report the real problem above
121 e = null; // shut Checkstyle up!
122 }
123 }
124 } catch (SQLException e) {
125 throw new SQLSeriousPoemException(e);
126 }
127 this.rows = rowsLocal;
128 updateSerials(token.transaction);
129 }
130 }
131
132 private boolean somethingHasChanged(PoemTransaction transaction) {
133 if (table.serial(transaction) != tableSerial)
134 return true;
135
136 if (otherTables != null) {
137 for (int i = 0; i < otherTables.length; i++) {
138 if (otherTables[i].serial(transaction) != otherTablesSerial[i])
139 return true;
140 }
141 }
142
143 return false;
144 }
145
146 private void updateSerials(PoemTransaction transaction) {
147 tableSerial = table.serial(transaction);
148 if (otherTables != null) {
149 for (int i = 0; i < otherTables.length; i++) {
150 otherTablesSerial[i] = otherTables[i].serial(transaction);
151 }
152 }
153 }
154
155 /**
156 * @return the table property
157 */
158 public Table<?> getTable() {
159 return table;
160 }
161
162 /**
163 * Used in constructor of {@link CachedSelection}.
164 * @param query the query to set
165 */
166 protected void setQuery(String query) {
167 this.query = query;
168 }
169
170 /**
171 * {@inheritDoc}
172 *
173 * @see java.lang.Object#toString()
174 */
175 public String toString() {
176 return getClass().getName() + " " + query;
177 }
178 }