View Javadoc
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 }