001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.flow.javascript;
018:
019: import java.sql.Connection;
020: import java.sql.PreparedStatement;
021: import java.sql.ResultSet;
022: import java.sql.ResultSetMetaData;
023: import java.sql.SQLException;
024:
025: import org.mozilla.javascript.Context;
026: import org.mozilla.javascript.Function;
027: import org.mozilla.javascript.JavaScriptException;
028: import org.mozilla.javascript.Scriptable;
029: import org.mozilla.javascript.ScriptableObject;
030: import org.mozilla.javascript.Undefined;
031: import org.mozilla.javascript.Wrapper;
032:
033: /**
034: * Wraps a JDBC connection and provides an API similar to JSTL
035: * A ScriptableConnection provides two methods:
036: *
037: * <UL>
038: * <LI>query([String] stmt, [Array] parameters, [Number] startRow, [Number] maxRows, [Function] fun)</LI>
039: * <LI>update([String] stmt, [Array] parameters)</LI>
040: * </UL>
041: * If the <code>fun</code> argument is provided to <code>query</code> it
042: * will be called for each row returned (the row object will be passed as its
043: * argument). For example:
044: * <pre>
045: * var db = Database.getConnection(...);
046: * var queryVal = ...;
047: * var startRow = 0;
048: * var maxRows = 100;
049: *
050: * db.query("select * from table where column = ?",
051: * [queryVal],
052: * startRow,
053: * maxRows,
054: * function(row) {
055: * print("column = " + row.column);
056: * });
057: *
058: * </pre>
059: * If <code>fun</code> is undefined, an object containing the following
060: * properties will be returned instead:
061: * <UL>
062: * <LI>[Array] rows - an array of row objects</LI>
063: * <LI>[Array] rowsByIndex - An array with an array per row of column values</LI>
064: * <LI>[Array] columnNames - An array of column names</LI>
065: * <LI>[Number] rowCount - Number of rows returned</LI>
066: * <LI>[Boolean] limitedByMaxRows - true if not all rows are included due to matching a maximum value </LI>
067: * </UL>
068: *
069: * A ScriptableConnection is also a wrapper around a real JDBC Connection and thus
070: * provides all of methods of Connection as well
071: *
072: * @version CVS $Id: ScriptableConnection.java 433543 2006-08-22 06:22:54Z crossley $
073: */
074: public class ScriptableConnection extends ScriptableObject {
075:
076: Connection connection;
077: Scriptable wrapper;
078:
079: static Object wrap(final Scriptable wrapper,
080: final Scriptable wrapped, Object obj) {
081: if (obj instanceof Function) {
082: return wrap(wrapper, wrapped, (Function) obj);
083: }
084: return obj;
085: }
086:
087: static Function wrap(final Scriptable wrapper,
088: final Scriptable wrapped, final Function fun) {
089: return new Function() {
090: public Object call(Context cx, Scriptable scope,
091: Scriptable this Obj, Object[] args)
092: throws JavaScriptException {
093: if (this Obj == wrapper) {
094: this Obj = wrapped;
095: }
096: return fun.call(cx, scope, this Obj, args);
097: }
098:
099: public Scriptable construct(Context cx, Scriptable scope,
100: Object[] args) throws JavaScriptException {
101: return fun.construct(cx, scope, args);
102: }
103:
104: public String getClassName() {
105: return fun.getClassName();
106: }
107:
108: public Object get(String name, Scriptable start) {
109: return fun.get(name, fun);
110: }
111:
112: public Object get(int index, Scriptable start) {
113: return fun.get(index, fun);
114: }
115:
116: public boolean has(String name, Scriptable start) {
117: return fun.has(name, start);
118: }
119:
120: public boolean has(int index, Scriptable start) {
121: return fun.has(index, start);
122: }
123:
124: public void put(String name, Scriptable start, Object value) {
125: fun.put(name, start, value);
126: }
127:
128: public void put(int index, Scriptable start, Object value) {
129: fun.put(index, start, value);
130: }
131:
132: public void delete(String name) {
133: fun.delete(name);
134: }
135:
136: public void delete(int index) {
137: fun.delete(index);
138: }
139:
140: public Scriptable getPrototype() {
141: return fun.getPrototype();
142: }
143:
144: public void setPrototype(Scriptable prototype) {
145: }
146:
147: public Scriptable getParentScope() {
148: return fun.getParentScope();
149: }
150:
151: public void setParentScope(Scriptable parent) {
152: }
153:
154: public Object[] getIds() {
155: return fun.getIds();
156: }
157:
158: public Object getDefaultValue(Class hint) {
159: return fun.getDefaultValue(hint);
160: }
161:
162: public boolean hasInstance(Scriptable instance) {
163: return fun.hasInstance(instance);
164: }
165:
166: };
167: }
168:
169: public String getClassName() {
170: return "Database";
171: }
172:
173: public ScriptableConnection() {
174: }
175:
176: public static void finishInit(Scriptable proto) {
177: }
178:
179: public static Scriptable jsConstructor(Context cx, Object[] args,
180: Function ctorObj, boolean inNewExpr) throws Exception {
181: Connection conn = null;
182: if (args.length > 0) {
183: Object arg = args[0];
184: if (arg instanceof Wrapper) {
185: arg = ((Wrapper) arg).unwrap();
186: }
187: if (arg instanceof Connection) {
188: conn = (Connection) arg;
189: }
190: }
191: if (conn == null) {
192: throw new JavaScriptException(
193: "expected an instance of java.sql.Connection");
194: }
195: ScriptableConnection result = new ScriptableConnection(ctorObj,
196: conn);
197: return result;
198: }
199:
200: public ScriptableConnection(Scriptable parent, Connection conn) {
201: this .connection = conn;
202: this .wrapper = Context.toObject(connection, parent);
203: }
204:
205: public Object jsFunction_query(String sql, Object params,
206: int startRow, int maxRows, Object funObj)
207: throws JavaScriptException {
208:
209: PreparedStatement stmt = null;
210: try {
211: stmt = connection.prepareStatement(sql);
212: Scriptable array = (Scriptable) params;
213: if (array != Undefined.instance) {
214: int len = (int) Context.toNumber(ScriptableObject
215: .getProperty(array, "length"));
216: for (int i = 0; i < len; i++) {
217: Object val = ScriptableObject.getProperty(array, i);
218: if (val instanceof Wrapper) {
219: val = ((Wrapper) val).unwrap();
220: }
221: if (val == Scriptable.NOT_FOUND) {
222: val = null;
223: }
224: stmt.setObject(i + 1, val);
225: }
226: }
227: ResultSet rs = stmt.executeQuery();
228: if (maxRows == 0) {
229: maxRows = -1;
230: }
231: if (funObj instanceof Function) {
232: Context cx = Context.getCurrentContext();
233: Function fun = (Function) funObj;
234: ResultSetMetaData rsmd = rs.getMetaData();
235: int noOfColumns = rsmd.getColumnCount();
236: // Throw away all rows upto startRow
237: for (int i = 0; i < startRow; i++) {
238: rs.next();
239: }
240: // Process the remaining rows upto maxRows
241: int processedRows = 0;
242: Scriptable scope = getTopLevelScope(this );
243: Scriptable proto = getObjectPrototype(scope);
244: Object[] args;
245: while (rs.next()) {
246: if ((maxRows != -1) && (processedRows == maxRows)) {
247: break;
248: }
249: Scriptable row = new ScriptableResult.Row();
250: row.setParentScope(scope);
251: row.setPrototype(proto);
252: for (int i = 1; i <= noOfColumns; i++) {
253: Object value = rs.getObject(i);
254: if (rs.wasNull()) {
255: value = null;
256: }
257: row.put(rsmd.getColumnName(i), row, value);
258: }
259: args = new Object[1];
260: args[0] = row;
261: fun.call(cx, scope, scope, args);
262: }
263: return Undefined.instance;
264: } else {
265: ScriptableResult s = new ScriptableResult(this , rs,
266: startRow, maxRows);
267: s.setParentScope(getTopLevelScope(this ));
268: s
269: .setPrototype(getClassPrototype(this , s
270: .getClassName()));
271: return s;
272: }
273: } catch (JavaScriptException e) {
274: throw e;
275: } catch (Exception e) {
276: throw new JavaScriptException(e);
277: } finally {
278: try {
279: if (stmt != null) {
280: stmt.close();
281: }
282: } catch (SQLException sqle) {
283: throw new JavaScriptException(sqle);
284: }
285: }
286: }
287:
288: public int jsFunction_update(String sql, Object params)
289: throws JavaScriptException {
290: PreparedStatement stmt = null;
291: try {
292: stmt = connection.prepareStatement(sql);
293: Scriptable array = (Scriptable) params;
294: if (array != Undefined.instance) {
295: int len = (int) Context.toNumber(ScriptableObject
296: .getProperty(array, "length"));
297: for (int i = 0; i < len; i++) {
298: Object val = ScriptableObject.getProperty(array, i);
299: if (val instanceof Wrapper) {
300: val = ((Wrapper) val).unwrap();
301: }
302: if (val == Scriptable.NOT_FOUND) {
303: val = null;
304: }
305: stmt.setObject(i + 1, val);
306: }
307: }
308: stmt.execute();
309: return stmt.getUpdateCount();
310: } catch (Exception e) {
311: throw new JavaScriptException(e);
312: } finally {
313: try {
314: if (stmt != null) {
315: stmt.close();
316: }
317: } catch (SQLException sqle) {
318: throw new JavaScriptException(sqle);
319: }
320: }
321: }
322:
323: public Object get(String name, Scriptable start) {
324: if (wrapper != null) {
325: Object result = wrapper.get(name, wrapper);
326: if (result != NOT_FOUND) {
327: return wrap(this , wrapper, result);
328: }
329: }
330: return super .get(name, start);
331: }
332:
333: public boolean has(String name, Scriptable start) {
334: if (wrapper != null) {
335: if (wrapper.has(name, wrapper)) {
336: return true;
337: }
338: }
339: return super .has(name, start);
340: }
341:
342: public boolean has(int index, Scriptable start) {
343: if (wrapper != null) {
344: if (wrapper.has(index, start)) {
345: return true;
346: }
347: }
348: return super .has(index, start);
349: }
350:
351: public Object get(int index, Scriptable start) {
352: if (wrapper != null) {
353: Object result = wrapper.get(index, start);
354: if (result != NOT_FOUND) {
355: return wrap(this , wrapper, result);
356: }
357: }
358: return super .get(index, start);
359: }
360:
361: public void put(String name, Scriptable start, Object value) {
362: if (wrapper != null) {
363: wrapper.put(name, wrapper, value);
364: return;
365: }
366: super.put(name, start, value);
367: }
368:
369: }
|