View Javadoc

1   /*
2    * Copyright 2002,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.commons.jelly.tags.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.sql.Statement;
24  
25  import javax.servlet.jsp.jstl.sql.Result;
26  
27  import org.apache.commons.jelly.JellyTagException;
28  import org.apache.commons.jelly.XMLOutput;
29  import org.apache.commons.jelly.tags.Resources;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  
34  /***
35   * <p>Tag handler for &lt;Query&gt; in JSTL.
36   *
37   * @author Hans Bergsten
38   * @author Justyna Horwat
39   */
40  
41  public class QueryTag extends SqlTagSupport {
42  
43      /*** The Log to which logging calls will be made. */
44      private static final Log log = LogFactory.getLog(QueryTag.class);
45  
46      /*
47       * The following properties take expression values, so the
48       * setter methods are implemented by the expression type
49       * specific subclasses.
50       */
51      protected int maxRows = -1;
52      protected boolean maxRowsSpecified;
53      protected int startRow;
54  
55      /*
56       * Instance variables that are not for attributes
57       */
58      private Connection conn;
59  
60      //**********************************************************************
61      // Constructor and initialization
62  
63      public QueryTag() {
64      }
65  
66      //*********************************************************************
67      // Accessor methods
68  
69      /**
70       * The index of the first row returned can be
71       * specified using startRow.
72       */
73      public void setStartRow(int startRow) {
74          this.startRow = startRow;
75      }
76  
77      /***
78       * Query result can be limited by specifying
79       * the maximum number of rows returned.
80       */
81      public void setMaxRows(int maxRows) {
82          this.maxRows = maxRows;
83          this.maxRowsSpecified = true;
84      }
85  
86      //**********************************************************************
87      // Tag logic
88  
89      /**
90       * <p>Execute the SQL statement, set either through the <code>sql</code>
91       * attribute or as the body, and save the result as a variable
92       * named by the <code>var</code> attribute in the scope specified
93       * by the <code>scope</code> attribute, as an object that implements
94       * the Result interface.
95       *
96       * <p>The connection used to execute the statement comes either
97       * from the <code>DataSource</code> specified by the
98       * <code>dataSource</code> attribute, provided by a parent action
99       * element, or is retrieved from a JSP scope  attribute
100      * named <code>javax.servlet.jstl.sql.dataSource</code>.
101      */
102     public void doTag(XMLOutput output) throws JellyTagException {
103 
104         if (!maxRowsSpecified) {
105             Object obj = context.getVariable("org.apache.commons.jelly.sql.maxRows");
106             if (obj != null) {
107                 if (obj instanceof Integer) {
108                     maxRows = ((Integer) obj).intValue();
109                 }
110                 else if (obj instanceof String) {
111                     try {
112                         maxRows = Integer.parseInt((String) obj);
113                     }
114                     catch (NumberFormatException nfe) {
115                         throw new JellyTagException(
116                             Resources.getMessage("SQL_MAXROWS_PARSE_ERROR", (String) obj),
117                             nfe);
118                     }
119                 }
120                 else {
121                     throw new JellyTagException(Resources.getMessage("SQL_MAXROWS_INVALID"));
122                 }
123             }
124         }
125 
126         Result result = null;
127         String sqlStatement = null;
128 
129         log.debug( "About to lookup connection" );
130 
131         ResultSet rs = null;
132         Statement statement = null;
133         try {
134             conn = getConnection();
135 
136             /*
137              * Use the SQL statement specified by the sql attribute, if any,
138              * otherwise use the body as the statement.
139              */
140             if (sql != null) {
141                 sqlStatement = sql;
142             }
143             else {
144                 sqlStatement = getBodyText();
145             }
146             if (sqlStatement == null || sqlStatement.trim().length() == 0) {
147                 throw new JellyTagException(Resources.getMessage("SQL_NO_STATEMENT"));
148             }
149             /*
150              * We shouldn't have a negative startRow or illegal maxrows
151              */
152             if ((startRow < 0) || (maxRows < -1)) {
153                 throw new JellyTagException(Resources.getMessage("PARAM_BAD_VALUE"));
154             }
155 
156             /*
157              * Note! We must not use the setMaxRows() method on the
158              * the statement to limit the number of rows, since the
159              * Result factory must be able to figure out the correct
160              * value for isLimitedByMaxRows(); there's no way to check
161              * if it was from the ResultSet.
162              */
163             if ( log.isDebugEnabled() ) {
164                 log.debug( "About to execute query: " + sqlStatement );
165             }
166 
167             if ( hasParameters() ) {
168                 PreparedStatement ps = conn.prepareStatement(sqlStatement);
169                 statement = ps;
170                 setParameters(ps);
171                 rs = ps.executeQuery();
172             }
173             else {
174                 statement = conn.createStatement();
175                 rs = statement.executeQuery(sqlStatement);
176             }
177 
178             result = new ResultImpl(rs, startRow, maxRows);
179             context.setVariable(var, result);
180 
181             // always close the result set first since it may be closed by
182             // JDBC 3 when closing statements
183 
184             // lets nullify before we close in case we get exceptions
185             // while closing, we don't want to try to close again
186             ResultSet tempRs = rs;
187             rs = null;
188             tempRs.close();
189             Statement tempStatement = statement;
190             statement = null;
191             tempStatement.close();
192         }
193         catch (SQLException e) {
194             throw new JellyTagException(sqlStatement + ": " + e.getMessage(), e);
195         }
196         finally {
197             if (rs != null) {
198                 try {
199                     rs.close();
200                 }
201                 catch (SQLException e) {
202                     log.error("Caught exception while closing result set: " + e, e);
203                 }
204             }
205             if (statement != null) {
206                 try {
207                     statement.close();
208                 }
209                 catch (SQLException e) {
210                     log.error("Caught exception while closing statement: " + e, e);
211                 }
212             }
213             if (conn != null && !isPartOfTransaction) {
214                 try {
215                     conn.close();
216                 }
217                 catch (SQLException e) {
218                     log.error("Caught exception while closing connection: " + e, e);
219                 }
220                 conn = null;
221             }
222             clearParameters();
223         }
224     }
225 }