001: /*
002: * argun 1.0
003: * Web 2.0 delivery framework
004: * Copyright (C) 2007 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.web.menu;
024:
025: import java.io.ByteArrayOutputStream;
026: import java.io.IOException;
027: import java.io.PrintStream;
028: import java.io.StringReader;
029: import java.sql.Connection;
030: import java.sql.ParameterMetaData;
031: import java.sql.PreparedStatement;
032: import java.sql.ResultSet;
033: import java.sql.ResultSetMetaData;
034: import java.sql.SQLException;
035: import java.util.ArrayList;
036: import java.util.Collection;
037: import java.util.HashMap;
038: import java.util.Map;
039: import java.util.Properties;
040:
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpServletResponse;
043: import javax.servlet.http.HttpSession;
044:
045: import org.apache.log4j.Logger;
046: import org.apache.xpath.CachedXPathAPI;
047: import org.w3c.dom.Element;
048:
049: import biz.hammurapi.cache.Cache;
050: import biz.hammurapi.cache.Entry;
051: import biz.hammurapi.codegen.GenerationException;
052: import biz.hammurapi.config.ConfigurationException;
053: import biz.hammurapi.config.Context;
054: import biz.hammurapi.sql.Parameterizer;
055: import biz.hammurapi.sql.Projector;
056: import biz.hammurapi.sql.SQLProcessor;
057: import biz.hammurapi.sql.columns.ObjectColumn;
058: import biz.hammurapi.sql.metadata.GenerationPolicy;
059: import biz.hammurapi.web.ActionsBase;
060: import biz.hammurapi.web.HammurapiWebException;
061: import biz.hammurapi.web.RequestContext;
062: import biz.hammurapi.web.menu.sql.MenuFunctionImpl;
063: import biz.hammurapi.web.security.AuthFilter;
064: import biz.hammurapi.web.util.DynaSQLProcessor;
065: import bsh.EvalError;
066: import bsh.Interpreter;
067:
068: public class Function extends MenuFunctionImpl implements Projector {
069: private static final String FT_JAVA = "Java";
070: private static final String FT_SINGLE_ROW = "Single row";
071: private static final String FT_QUERY = "Query";
072: private static final String FT_UPDATE = "Update";
073: private static final String FT_SCRIPT = "Script";
074:
075: private static final Logger logger = Logger
076: .getLogger(Function.class);
077:
078: private Menu owner;
079: private SQLProcessor processor;
080:
081: public Function() {
082: super ();
083: }
084:
085: public Function(boolean force) {
086: super (force);
087: }
088:
089: public Function(Element holder, boolean force)
090: throws ConfigurationException {
091: super (holder, force);
092: }
093:
094: public Function(Element holder, Properties pathMap,
095: CachedXPathAPI cxpa, boolean force)
096: throws ConfigurationException {
097: super (holder, pathMap, cxpa, force);
098: }
099:
100: public Function(ResultSet rs) throws SQLException {
101: super (rs);
102: }
103:
104: public Object project(ResultSet rs) throws SQLException {
105: if (columnMap.size() == 1) {
106: return rs.getString(1);
107: } else {
108: return DynaSQLProcessor.project(rs, resultInterface,
109: columnMap);
110: }
111: }
112:
113: public Object invoke(final Object[] args)
114: throws HammurapiWebException {
115: return invoke(args, null, null);
116: }
117:
118: public Object invoke(final Object[] args,
119: HttpServletRequest request, HttpServletResponse response)
120: throws HammurapiWebException {
121: final int actualArgsCount = args == null ? 0 : args.length;
122: if (actualArgsCount != getParameterCount()) {
123: throw new HammurapiWebException(
124: "Wrong number of parameters");
125: }
126:
127: if (FT_JAVA.equals(getType())) {
128: Interpreter interpreter = new Interpreter();
129: interpreter.setClassLoader(owner.getInjectingClassLoader());
130: try {
131: interpreter.set("menu", owner);
132: interpreter.set("engine", owner.getRuntimeEngine());
133: interpreter.set("request", request);
134: interpreter.set("response", response);
135: interpreter.set("converter", ActionsBase.converter);
136: HttpSession session = request.getSession();
137: Object user = session.getAttribute(AuthFilter.USER);
138: if (user != null) {
139: interpreter.set("user", user);
140:
141: }
142: Object authProvider = session
143: .getAttribute(AuthFilter.AUTHORIZATION_PROVIDER);
144: if (authProvider != null) {
145: interpreter.set("authorizationProvider",
146: authProvider);
147: }
148:
149: final RequestContext requestContext = new RequestContext(
150: request);
151: Context menuContext = new Context() {
152:
153: public Object get(String key) {
154: return owner.get(key, requestContext);
155: }
156:
157: };
158:
159: interpreter.set("requestContext", requestContext);
160: interpreter.set("menuContext", menuContext);
161: interpreter.set(MenuFilter.CE_GLOBAL, request
162: .getAttribute("global"));
163:
164: for (int i = 0; i < actualArgsCount; ++i) {
165: if (args[i] != null) {
166: interpreter.set("arg" + i, args[i]);
167: }
168: }
169:
170: ByteArrayOutputStream baos = null;
171: if (response != null) {
172: baos = new ByteArrayOutputStream();
173: PrintStream out = new PrintStream(baos);
174: interpreter.setOut(out);
175: interpreter.setErr(out);
176: }
177:
178: try {
179: return interpreter.eval(getFunctionCode());
180: } finally {
181: if (baos != null) {
182: try {
183: baos.close();
184: response.getWriter().write(
185: new String(baos.toByteArray()));
186: } catch (IOException e) {
187: throw new HammurapiWebException(
188: "Could not write interpreter output to response: "
189: + e, e);
190: }
191: }
192: }
193: } catch (EvalError e) {
194: throw new HammurapiWebException(
195: "Could not execute function: " + e, e);
196: }
197: } else {
198: Parameterizer parameterizer = new Parameterizer() {
199:
200: public void parameterize(PreparedStatement ps)
201: throws SQLException {
202: for (int i = 0; i < actualArgsCount; ++i) {
203: ObjectColumn.parameterize(ps, args[i], i + 1);
204: }
205: }
206:
207: };
208:
209: try {
210: if (FT_SINGLE_ROW.equals(getType())) {
211: Cache cache = owner.getCache();
212: if (getCacheInterval() <= 0 || cache == null) {
213: return processor.projectSingleObject(
214: getFunctionCode(), parameterizer, this );
215: }
216:
217: Object key = cacheKey(args);
218: Entry cacheEntry = cache.get(key);
219: if (cacheEntry != null) {
220: return cacheEntry.get();
221: }
222:
223: Object ret = processor.projectSingleObject(
224: getFunctionCode(), parameterizer, this );
225: long now = System.currentTimeMillis();
226: cache.put(key, ret, now, now + getCacheInterval());
227: return ret;
228: } else if (FT_QUERY.equals(getType())) {
229: Cache cache = owner.getCache();
230: if (getCacheInterval() <= 0 || cache == null) {
231: return processor.project(getFunctionCode(),
232: parameterizer, this , new ArrayList());
233: }
234:
235: Object key = cacheKey(args);
236: Entry cacheEntry = cache.get(key);
237: if (cacheEntry != null) {
238: return cacheEntry.get();
239: }
240:
241: Object ret = processor.project(getFunctionCode(),
242: parameterizer, this , new ArrayList());
243: long now = System.currentTimeMillis();
244: cache.put(key, ret, now, now + getCacheInterval());
245: return ret;
246: } else if (FT_UPDATE.equals(getType())) {
247: return new Integer(processor.processUpdate(
248: getFunctionCode(), parameterizer));
249: } else if (FT_SCRIPT.equals(getType())) {
250: processor.executeScript(new StringReader(
251: getFunctionCode()));
252: return null;
253: }
254: } catch (SQLException e) {
255: throw new HammurapiWebException("Could not execute "
256: + getFunctionCode() + ": " + e, e);
257: } catch (IOException e) {
258: throw new HammurapiWebException("Could not execute "
259: + getFunctionCode() + ": " + e, e);
260: }
261: }
262:
263: throw new HammurapiWebException("Unsupported function type: "
264: + getType());
265: }
266:
267: private Object cacheKey(final Object[] args) {
268: Collection ret = new ArrayList();
269: ret.add("Function cache");
270: ret.add(ownerId);
271: ret.add(getName());
272: for (int i = 0; i < args.length; ++i) {
273: ret.add(args[i]);
274: }
275: return ret;
276: }
277:
278: private Class resultInterface;
279: private Integer ownerId;
280:
281: public void start(Context context, Menu owner)
282: throws HammurapiWebException {
283: this .owner = owner;
284: ownerId = new Integer(owner.getId());
285: if (FT_JAVA.equals(getType())) {
286: // Nothing
287: } else {
288: processor = (SQLProcessor) context.get(getSqlProcessor());
289: if (processor == null) {
290: throw new HammurapiWebException("SQL processor '"
291: + getSqlProcessor() + "' is not found");
292: }
293:
294: if (!FT_SCRIPT.equals(getType())) {
295: try {
296: Connection con = processor.getConnection();
297: try {
298: PreparedStatement pst = con
299: .prepareStatement(getFunctionCode());
300: try {
301: if (!FT_UPDATE.equals(getType())) {
302: ResultSetMetaData metadata = pst
303: .getMetaData();
304: if (metadata == null) {
305: throw new HammurapiWebException(
306: "Could not retrieve result set metadata for SQL statement "
307: + getFunctionCode());
308: }
309:
310: GenerationPolicy gp = owner
311: .getGenerationPolicy();
312: for (int i = 1, j = metadata
313: .getColumnCount(); i <= j; ++i) {
314: String columnName = metadata
315: .getColumnName(i);
316: columnMap
317: .put(
318: gp
319: .generateColumnName(columnName),
320: columnName);
321: }
322:
323: if (columnMap.size() > 1) {
324: try {
325: resultInterface = DynaSQLProcessor
326: .generateResultSetInterface(
327: Menu.class
328: .getName()
329: + "$RuntimeEngine"
330: + owner
331: .getId()
332: + "$"
333: + getName(),
334: columnMap,
335: owner
336: .getInterfacePool(),
337: owner
338: .getInjectingClassLoader());
339:
340: } catch (GenerationException e) {
341: throw new HammurapiWebException(
342: "Could not generate dynamic interface for query "
343: + getFunctionCode()
344: + ": " + e, e);
345: } catch (ClassNotFoundException e) {
346: throw new HammurapiWebException(
347: "Could not load dynamic interface for query "
348: + getFunctionCode()
349: + ": " + e, e);
350: }
351: }
352: }
353:
354: try {
355: ParameterMetaData parameterMetaData = pst
356: .getParameterMetaData();
357: setParameterCount(parameterMetaData
358: .getParameterCount());
359: } catch (SQLException e) {
360: logger
361: .warn(
362: "JDBC driver doesn't support getParameterMetadata(), using number of parameters specified in the function definition",
363: e);
364: }
365: } finally {
366: pst.close();
367: }
368: } finally {
369: con.close();
370: }
371: } catch (SQLException e) {
372: throw new HammurapiWebException(
373: "Could not prepare SQL statement "
374: + getFunctionCode() + ": " + e, e);
375: }
376: }
377: }
378: }
379:
380: // Java name -> column name
381: private Map columnMap = new HashMap();
382: }
|