Source code: org/apache/taglibs/standard/tag/common/sql/QueryTagSupport.java
1 /*
2 * Copyright 1999-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.taglibs.standard.tag.common.sql;
18
19 import java.sql.Connection;
20 import java.sql.PreparedStatement;
21 import java.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.servlet.jsp.JspException;
27 import javax.servlet.jsp.JspTagException;
28 import javax.servlet.jsp.PageContext;
29 import javax.servlet.jsp.jstl.core.Config;
30 import javax.servlet.jsp.jstl.sql.Result;
31 import javax.servlet.jsp.jstl.sql.SQLExecutionTag;
32 import javax.servlet.jsp.tagext.BodyTagSupport;
33 import javax.servlet.jsp.tagext.TryCatchFinally;
34 import javax.sql.DataSource;
35
36 import org.apache.taglibs.standard.resources.Resources;
37 import org.apache.taglibs.standard.tag.common.core.Util;
38
39
40 /**
41 * <p>Tag handler for <Query> in JSTL.
42 *
43 * @author Hans Bergsten
44 * @author Justyna Horwat
45 */
46
47 public abstract class QueryTagSupport extends BodyTagSupport
48 implements TryCatchFinally, SQLExecutionTag {
49
50 private String var;
51 private int scope;
52
53 /*
54 * The following properties take expression values, so the
55 * setter methods are implemented by the expression type
56 * specific subclasses.
57 */
58 protected Object rawDataSource;
59 protected boolean dataSourceSpecified;
60 protected String sql;
61 protected int maxRows;
62 protected boolean maxRowsSpecified;
63 protected int startRow;
64
65 /*
66 * Instance variables that are not for attributes
67 */
68 private Connection conn;
69 private List parameters;
70 private boolean isPartOfTransaction;
71
72
73 //*********************************************************************
74 // Constructor and initialization
75
76 public QueryTagSupport() {
77 super();
78 init();
79 }
80
81 private void init() {
82 startRow = 0;
83 maxRows = -1;
84 maxRowsSpecified = dataSourceSpecified = false;
85 isPartOfTransaction = false;
86 conn = null;
87 rawDataSource = null;
88 parameters = null;
89 sql = null;
90 var = null;
91 scope = PageContext.PAGE_SCOPE;
92 }
93
94
95 //*********************************************************************
96 // Accessor methods
97
98 /**
99 * Setter method for the name of the variable to hold the
100 * result.
101 */
102 public void setVar(String var) {
103 this.var = var;
104 }
105
106 /**
107 * Setter method for the scope of the variable to hold the
108 * result.
109 */
110 public void setScope(String scopeName) {
111 scope = Util.getScope(scopeName);
112 }
113
114 //*********************************************************************
115 // Public utility methods
116
117 /**
118 * Called by nested parameter elements to add PreparedStatement
119 * parameter values.
120 */
121 public void addSQLParameter(Object o) {
122 if (parameters == null) {
123 parameters = new ArrayList();
124 }
125 parameters.add(o);
126 }
127
128 //*********************************************************************
129 // Tag logic
130
131 /**
132 * Prepares for execution by setting the initial state, such as
133 * getting the <code>Connection</code>
134 */
135 public int doStartTag() throws JspException {
136
137 if (!maxRowsSpecified) {
138 Object obj = Config.find(pageContext, Config.SQL_MAX_ROWS);
139 if (obj != null) {
140 if (obj instanceof Integer) {
141 maxRows = ((Integer) obj).intValue();
142 } else if (obj instanceof String) {
143 try {
144 maxRows = Integer.parseInt((String) obj);
145 } catch (NumberFormatException nfe) {
146 throw new JspException(
147 Resources.getMessage("SQL_MAXROWS_PARSE_ERROR",
148 (String) obj),
149 nfe);
150 }
151 } else {
152 throw new JspException(
153 Resources.getMessage("SQL_MAXROWS_INVALID"));
154 }
155 }
156 }
157
158 try {
159 conn = getConnection();
160 } catch (SQLException e) {
161 throw new JspException(sql + ": " + e.getMessage(), e);
162 }
163
164 return EVAL_BODY_BUFFERED;
165 }
166
167 /**
168 * <p>Execute the SQL statement, set either through the <code>sql</code>
169 * attribute or as the body, and save the result as a variable
170 * named by the <code>var</code> attribute in the scope specified
171 * by the <code>scope</code> attribute, as an object that implements
172 * the Result interface.
173 *
174 * <p>The connection used to execute the statement comes either
175 * from the <code>DataSource</code> specified by the
176 * <code>dataSource</code> attribute, provided by a parent action
177 * element, or is retrieved from a JSP scope attribute
178 * named <code>javax.servlet.jstl.sql.dataSource</code>.
179 */
180 public int doEndTag() throws JspException {
181 /*
182 * Use the SQL statement specified by the sql attribute, if any,
183 * otherwise use the body as the statement.
184 */
185 String sqlStatement = null;
186 if (sql != null) {
187 sqlStatement = sql;
188 }
189 else if (bodyContent != null) {
190 sqlStatement = bodyContent.getString();
191 }
192 if (sqlStatement == null || sqlStatement.trim().length() == 0) {
193 throw new JspTagException(
194 Resources.getMessage("SQL_NO_STATEMENT"));
195 }
196 /*
197 * We shouldn't have a negative startRow or illegal maxrows
198 */
199 if ((startRow < 0) || (maxRows < -1)) {
200 throw new JspException(
201 Resources.getMessage("PARAM_BAD_VALUE"));
202 }
203
204 Result result = null;
205 /*
206 * Note! We must not use the setMaxRows() method on the
207 * the statement to limit the number of rows, since the
208 * Result factory must be able to figure out the correct
209 * value for isLimitedByMaxRows(); there's no way to check
210 * if it was from the ResultSet.
211 */
212 try {
213 PreparedStatement ps = conn.prepareStatement(sqlStatement);
214 setParameters(ps, parameters);
215 ResultSet rs = ps.executeQuery();
216 result = new ResultImpl(rs, startRow, maxRows);
217 ps.close();
218 }
219 catch (Throwable e) {
220 throw new JspException(sqlStatement + ": " + e.getMessage(), e);
221 }
222 pageContext.setAttribute(var, result, scope);
223 return EVAL_PAGE;
224 }
225
226 /**
227 * Just rethrows the Throwable.
228 */
229 public void doCatch(Throwable t) throws Throwable {
230 throw t;
231 }
232
233 /**
234 * Close the <code>Connection</code>, unless this action is used
235 * as part of a transaction.
236 */
237 public void doFinally() {
238 if (conn != null && !isPartOfTransaction) {
239 try {
240 conn.close();
241 } catch (SQLException e) {} // Not much we can do
242 }
243
244 conn = null;
245 parameters = null;
246 }
247
248
249 //*********************************************************************
250 // Private utility methods
251
252 private Connection getConnection() throws JspException, SQLException {
253 // Fix: Add all other mechanisms
254 Connection conn = null;
255 isPartOfTransaction = false;
256
257 TransactionTagSupport parent = (TransactionTagSupport)
258 findAncestorWithClass(this, TransactionTagSupport.class);
259 if (parent != null) {
260 if (dataSourceSpecified) {
261 throw new JspTagException(
262 Resources.getMessage("ERROR_NESTED_DATASOURCE"));
263 }
264 conn = parent.getSharedConnection();
265 isPartOfTransaction = true;
266 } else {
267 if ((rawDataSource == null) && dataSourceSpecified) {
268 throw new JspException(
269 Resources.getMessage("SQL_DATASOURCE_NULL"));
270 }
271 DataSource dataSource = DataSourceUtil.getDataSource(rawDataSource,
272 pageContext);
273 try {
274 conn = dataSource.getConnection();
275 } catch (Exception ex) {
276 throw new JspException(
277 Resources.getMessage("DATASOURCE_INVALID",
278 ex.toString()));
279 }
280 }
281
282 return conn;
283 }
284
285 private void setParameters(PreparedStatement ps, List parameters)
286 throws SQLException
287 {
288 if (parameters != null) {
289 for (int i = 0; i < parameters.size(); i++) {
290 /* The first parameter has index 1. If a null
291 * is passed to setObject the parameter will be
292 * set to JDBC null so an explicit call to
293 * ps.setNull is not required.
294 */
295 ps.setObject(i + 1, parameters.get(i));
296 }
297 }
298 }
299 }