001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Portions Copyright Apache Software Foundation.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common Development
010: * and Distribution License("CDDL") (collectively, the "License"). You
011: * may not use this file except in compliance with the License. You can obtain
012: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
013: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
014: * language governing permissions and limitations under the License.
015: *
016: * When distributing the software, include this License Header Notice in each
017: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
018: * Sun designates this particular file as subject to the "Classpath" exception
019: * as provided by Sun in the GPL Version 2 section of the License file that
020: * accompanied this code. If applicable, add the following below the License
021: * Header, with the fields enclosed by brackets [] replaced by your own
022: * identifying information: "Portions Copyrighted [year]
023: * [name of copyright owner]"
024: *
025: * Contributor(s):
026: *
027: * If you wish your version of this file to be governed by only the CDDL or
028: * only the GPL Version 2, indicate your decision by adding "[Contributor]
029: * elects to include this software in this distribution under the [CDDL or GPL
030: * Version 2] license." If you don't indicate a single choice of license, a
031: * recipient has the option to distribute your version of this file under
032: * either the CDDL, the GPL Version 2 or to extend the choice of license to
033: * its licensees as provided above. However, if you add GPL Version 2 code
034: * and therefore, elected the GPL Version 2 license, then the option applies
035: * only if the new code is made subject to such option by the copyright
036: * holder.
037: */
038:
039: package org.apache.taglibs.standard.tag.common.sql;
040:
041: import java.sql.Connection;
042: import java.sql.PreparedStatement;
043: import java.sql.ResultSet;
044: import java.sql.SQLException;
045: import java.util.ArrayList;
046: import java.util.List;
047:
048: import javax.servlet.jsp.JspException;
049: import javax.servlet.jsp.JspTagException;
050: import javax.servlet.jsp.PageContext;
051: import javax.servlet.jsp.jstl.core.Config;
052: import javax.servlet.jsp.jstl.sql.Result;
053: import javax.servlet.jsp.jstl.sql.SQLExecutionTag;
054: import javax.servlet.jsp.tagext.BodyTagSupport;
055: import javax.servlet.jsp.tagext.TryCatchFinally;
056: import javax.sql.DataSource;
057:
058: import org.apache.taglibs.standard.resources.Resources;
059: import org.apache.taglibs.standard.tag.common.core.Util;
060:
061: /**
062: * <p>Tag handler for <Query> in JSTL.
063: *
064: * @author Hans Bergsten
065: * @author Justyna Horwat
066: */
067:
068: public abstract class QueryTagSupport extends BodyTagSupport implements
069: TryCatchFinally, SQLExecutionTag {
070:
071: private String var;
072: private int scope;
073:
074: /*
075: * The following properties take expression values, so the
076: * setter methods are implemented by the expression type
077: * specific subclasses.
078: */
079: protected Object rawDataSource;
080: protected boolean dataSourceSpecified;
081: protected String sql;
082: protected int maxRows;
083: protected boolean maxRowsSpecified;
084: protected int startRow;
085:
086: /*
087: * Instance variables that are not for attributes
088: */
089: private Connection conn;
090: private List parameters;
091: private boolean isPartOfTransaction;
092:
093: //*********************************************************************
094: // Constructor and initialization
095:
096: public QueryTagSupport() {
097: super ();
098: init();
099: }
100:
101: private void init() {
102: startRow = 0;
103: maxRows = -1;
104: maxRowsSpecified = dataSourceSpecified = false;
105: isPartOfTransaction = false;
106: conn = null;
107: rawDataSource = null;
108: parameters = null;
109: sql = null;
110: var = null;
111: scope = PageContext.PAGE_SCOPE;
112: }
113:
114: //*********************************************************************
115: // Accessor methods
116:
117: /**
118: * Setter method for the name of the variable to hold the
119: * result.
120: */
121: public void setVar(String var) {
122: this .var = var;
123: }
124:
125: /**
126: * Setter method for the scope of the variable to hold the
127: * result.
128: */
129: public void setScope(String scopeName) {
130: scope = Util.getScope(scopeName);
131: }
132:
133: //*********************************************************************
134: // Public utility methods
135:
136: /**
137: * Called by nested parameter elements to add PreparedStatement
138: * parameter values.
139: */
140: public void addSQLParameter(Object o) {
141: if (parameters == null) {
142: parameters = new ArrayList();
143: }
144: parameters.add(o);
145: }
146:
147: //*********************************************************************
148: // Tag logic
149:
150: /**
151: * Prepares for execution by setting the initial state, such as
152: * getting the <code>Connection</code>
153: */
154: public int doStartTag() throws JspException {
155:
156: if (!maxRowsSpecified) {
157: Object obj = Config.find(pageContext, Config.SQL_MAX_ROWS);
158: if (obj != null) {
159: if (obj instanceof Integer) {
160: maxRows = ((Integer) obj).intValue();
161: } else if (obj instanceof String) {
162: try {
163: maxRows = Integer.parseInt((String) obj);
164: } catch (NumberFormatException nfe) {
165: throw new JspException(Resources
166: .getMessage("SQL_MAXROWS_PARSE_ERROR",
167: (String) obj), nfe);
168: }
169: } else {
170: throw new JspException(Resources
171: .getMessage("SQL_MAXROWS_INVALID"));
172: }
173: }
174: }
175:
176: try {
177: conn = getConnection();
178: } catch (SQLException e) {
179: throw new JspException(sql + ": " + e.getMessage(), e);
180: }
181:
182: return EVAL_BODY_BUFFERED;
183: }
184:
185: /**
186: * <p>Execute the SQL statement, set either through the <code>sql</code>
187: * attribute or as the body, and save the result as a variable
188: * named by the <code>var</code> attribute in the scope specified
189: * by the <code>scope</code> attribute, as an object that implements
190: * the Result interface.
191: *
192: * <p>The connection used to execute the statement comes either
193: * from the <code>DataSource</code> specified by the
194: * <code>dataSource</code> attribute, provided by a parent action
195: * element, or is retrieved from a JSP scope attribute
196: * named <code>javax.servlet.jstl.sql.dataSource</code>.
197: */
198: public int doEndTag() throws JspException {
199: /*
200: * Use the SQL statement specified by the sql attribute, if any,
201: * otherwise use the body as the statement.
202: */
203: String sqlStatement = null;
204: if (sql != null) {
205: sqlStatement = sql;
206: } else if (bodyContent != null) {
207: sqlStatement = bodyContent.getString();
208: }
209: if (sqlStatement == null || sqlStatement.trim().length() == 0) {
210: throw new JspTagException(Resources
211: .getMessage("SQL_NO_STATEMENT"));
212: }
213: /*
214: * We shouldn't have a negative startRow or illegal maxrows
215: */
216: if ((startRow < 0) || (maxRows < -1)) {
217: throw new JspException(Resources
218: .getMessage("PARAM_BAD_VALUE"));
219: }
220:
221: Result result = null;
222: /*
223: * Note! We must not use the setMaxRows() method on the
224: * the statement to limit the number of rows, since the
225: * Result factory must be able to figure out the correct
226: * value for isLimitedByMaxRows(); there's no way to check
227: * if it was from the ResultSet.
228: */
229: try {
230: PreparedStatement ps = conn.prepareStatement(sqlStatement);
231: setParameters(ps, parameters);
232: ResultSet rs = ps.executeQuery();
233: result = new ResultImpl(rs, startRow, maxRows);
234: ps.close();
235: } catch (Throwable e) {
236: throw new JspException(
237: sqlStatement + ": " + e.getMessage(), e);
238: }
239: pageContext.setAttribute(var, result, scope);
240: return EVAL_PAGE;
241: }
242:
243: /**
244: * Just rethrows the Throwable.
245: */
246: public void doCatch(Throwable t) throws Throwable {
247: throw t;
248: }
249:
250: /**
251: * Close the <code>Connection</code>, unless this action is used
252: * as part of a transaction.
253: */
254: public void doFinally() {
255: if (conn != null && !isPartOfTransaction) {
256: try {
257: conn.close();
258: } catch (SQLException e) {
259: } // Not much we can do
260: }
261:
262: conn = null;
263: parameters = null;
264: }
265:
266: //*********************************************************************
267: // Private utility methods
268:
269: private Connection getConnection() throws JspException,
270: SQLException {
271: // Fix: Add all other mechanisms
272: Connection conn = null;
273: isPartOfTransaction = false;
274:
275: TransactionTagSupport parent = (TransactionTagSupport) findAncestorWithClass(
276: this , TransactionTagSupport.class);
277: if (parent != null) {
278: if (dataSourceSpecified) {
279: throw new JspTagException(Resources
280: .getMessage("ERROR_NESTED_DATASOURCE"));
281: }
282: conn = parent.getSharedConnection();
283: isPartOfTransaction = true;
284: } else {
285: if ((rawDataSource == null) && dataSourceSpecified) {
286: throw new JspException(Resources
287: .getMessage("SQL_DATASOURCE_NULL"));
288: }
289: DataSource dataSource = DataSourceUtil.getDataSource(
290: rawDataSource, pageContext);
291: try {
292: conn = dataSource.getConnection();
293: } catch (Exception ex) {
294: throw new JspException(Resources.getMessage(
295: "DATASOURCE_INVALID", ex.toString()));
296: }
297: }
298:
299: return conn;
300: }
301:
302: private void setParameters(PreparedStatement ps, List parameters)
303: throws SQLException {
304: if (parameters != null) {
305: for (int i = 0; i < parameters.size(); i++) {
306: /* The first parameter has index 1. If a null
307: * is passed to setObject the parameter will be
308: * set to JDBC null so an explicit call to
309: * ps.setNull is not required.
310: */
311: ps.setObject(i + 1, parameters.get(i));
312: }
313: }
314: }
315: }
|