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;
024:
025: import java.io.IOException;
026: import java.io.PrintStream;
027: import java.io.PrintWriter;
028: import java.io.StringReader;
029: import java.io.StringWriter;
030: import java.rmi.server.UID;
031: import java.sql.ResultSet;
032: import java.sql.ResultSetMetaData;
033: import java.sql.SQLException;
034: import java.util.ArrayList;
035: import java.util.Collection;
036: import java.util.HashMap;
037: import java.util.Iterator;
038: import java.util.Map;
039:
040: import javax.servlet.ServletOutputStream;
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.w3c.dom.Element;
047:
048: import biz.hammurapi.authorization.AuthorizationProvider;
049: import biz.hammurapi.config.Context;
050: import biz.hammurapi.convert.CompositeConverter;
051: import biz.hammurapi.sql.MetadataAwareRowProcessor;
052: import biz.hammurapi.sql.SQLProcessor;
053: import biz.hammurapi.util.Attributable;
054: import biz.hammurapi.web.menu.Menu;
055: import biz.hammurapi.web.menu.MenuFilter;
056: import biz.hammurapi.web.remoting.Registry;
057: import biz.hammurapi.web.security.AuthFilter;
058: import biz.hammurapi.web.util.HttpRequestDomSerializer;
059: import biz.hammurapi.xml.dom.CompositeDomSerializer;
060: import biz.hammurapi.xml.dom.DomSerializable;
061: import bsh.Interpreter;
062:
063: /**
064: * This class contains convenience methods for action classes.
065: * @author Pavel Vlasov
066: * @revision $Revision$
067: */
068: public class ActionsBase {
069: private static final Logger logger = Logger
070: .getLogger(ActionsBase.class);
071: private static final String AUTHORIZATION_PROVIDER_ATTRIBUTE = AuthorizationProvider.class
072: .getName();
073:
074: protected static final String REFERRER_HEADER = "referer";
075:
076: private String uid = new UID().toString();
077:
078: /**
079: * Converter for Web types.
080: */
081: public static CompositeConverter converter = new CompositeConverter() {
082: public Object convert(Object source, Class target,
083: boolean lenient) {
084: if ("".equals(source)
085: && Number.class.isAssignableFrom(target)) {
086: return null;
087: }
088:
089: if (Boolean.class.isAssignableFrom(target)
090: || boolean.class.isAssignableFrom(target)) {
091: if ("".equals(source) || source == null) {
092: return Boolean.FALSE;
093: }
094:
095: if ("on".equalsIgnoreCase(source.toString())) {
096: return Boolean.TRUE;
097: }
098: }
099:
100: return super .convert(source, target, lenient);
101: }
102: };
103:
104: protected String getUid() {
105: return uid;
106: }
107:
108: protected void setReferrer(HttpServletRequest request,
109: Attributable target) {
110: String referrer = request.getHeader(REFERRER_HEADER);
111: if (referrer != null) {
112: target.setAttribute("referrer", referrer);
113: }
114: }
115:
116: public static class ErrorMessage implements DomSerializable {
117: private String msg;
118:
119: /**
120: * @param msg
121: */
122: ErrorMessage(String msg) {
123: super ();
124: // TODO Auto-generated constructor stub
125: this .msg = msg;
126: }
127:
128: public void toDom(Element holder) {
129: holder.setAttribute("type", "error-message");
130: holder.appendChild(holder.getOwnerDocument()
131: .createTextNode(msg));
132: }
133:
134: }
135:
136: /**
137: * Helper method
138: * @param request
139: * @param name
140: * @return
141: */
142: protected static Object getGlobal(HttpServletRequest request,
143: String name) {
144: return ((Context) request.getAttribute("global")).get(name);
145: }
146:
147: public static boolean isBlank(String str) {
148: return str == null || str.trim().length() == 0;
149: }
150:
151: protected static final CompositeDomSerializer compositeDomSerializer;
152:
153: static {
154: compositeDomSerializer = new CompositeDomSerializer();
155: compositeDomSerializer
156: .addDomSerializer(new HttpRequestDomSerializer());
157: }
158:
159: /**
160: * Serializes request to XML. This aciton is useful for pages
161: * which don't require any information from back-end, but require
162: * request parameters and other request info for rendering.
163: * @param request
164: * @param response
165: * @param servlet
166: * @return
167: */
168: public DomSerializable request2xml(HttpServletRequest request,
169: HttpServletResponse response) {
170: CompositeDomSerializer
171: .pushThreadSerializer(compositeDomSerializer);
172: try {
173: return compositeDomSerializer.toDomSerializable(request);
174: } finally {
175: CompositeDomSerializer.popThreadSerializer();
176: }
177: }
178:
179: protected static boolean includeThisInPath(
180: HttpServletRequest request) {
181: return "yes".equals(getGlobal(request, "db/IncludeThisInPath"));
182: }
183:
184: /**
185: * Returns value of "_referrer" request parameter. If parameter is not set
186: * then returns value of referer header.
187: * @param request
188: * @return
189: */
190: public static String getReferrer(HttpServletRequest request) {
191: String ret = request.getParameter("_referrer");
192: return ret == null ? request.getHeader(REFERRER_HEADER) : ret;
193: }
194:
195: protected boolean hasPermission(HttpServletRequest request,
196: Class clazz, String action) {
197: Object ap = request
198: .getAttribute(AUTHORIZATION_PROVIDER_ATTRIBUTE);
199: if (ap == null) {
200: HttpSession session = request.getSession(false);
201: ap = session == null ? null : session
202: .getAttribute(AUTHORIZATION_PROVIDER_ATTRIBUTE);
203: }
204:
205: if (ap instanceof AuthorizationProvider) {
206: return ((AuthorizationProvider) ap).hasClassPermission(
207: clazz, action);
208: }
209:
210: if (ap != null) {
211: logger
212: .warn("Configuration problem with authorization provider: "
213: + ap.getClass().getName()
214: + " does not implement "
215: + AuthorizationProvider.class.getName());
216: return false;
217: }
218:
219: return true;
220: }
221:
222: /**
223: * Executes java script passed in "java" parameter, writes output to response stream.
224: * @param request
225: * @param response
226: * @param servlet
227: * @return
228: * @throws IOException
229: */
230: public void java(HttpServletRequest request,
231: HttpServletResponse response, ActionServlet servlet)
232: throws IOException {
233: String code = request.getParameter("java");
234: if (code == null) {
235: response.getOutputStream().write(
236: "Nothing to execute".getBytes());
237: } else {
238: response.setContentType("text/plain");
239: ServletOutputStream out = response.getOutputStream();
240: PrintStream ps = new PrintStream(out);
241: try {
242: HttpSession session = request.getSession();
243: Interpreter interpreter = (Interpreter) session
244: .getAttribute(Interpreter.class.getName());
245: if (interpreter == null) {
246: interpreter = new Interpreter();
247: session.setAttribute(Interpreter.class.getName(),
248: interpreter);
249: }
250:
251: synchronized (interpreter) {
252: interpreter.set("requestContext",
253: new RequestContext(request));
254: interpreter.set("request", request);
255: interpreter.set("response", response);
256: interpreter.set(MenuFilter.CE_GLOBAL, request
257: .getAttribute("global"));
258: Object menu = session
259: .getAttribute(MenuFilter.MENU_ATTRIBUTE);
260: if (menu != null) {
261: interpreter.set("menu", menu);
262: }
263: Object user = session.getAttribute(AuthFilter.USER);
264: if (user != null) {
265: interpreter.set("user", user);
266:
267: }
268: Object authProvider = session
269: .getAttribute(AuthFilter.AUTHORIZATION_PROVIDER);
270: if (authProvider != null) {
271: interpreter.set("authorizationProvider",
272: authProvider);
273: }
274:
275: interpreter.set("converter", converter);
276:
277: interpreter.setOut(ps);
278: interpreter.setErr(ps);
279:
280: Object ret = interpreter.eval(code);
281: if (ret != null) {
282: ps.println(ret);
283: }
284: }
285: } catch (Exception e) {
286: e.printStackTrace(ps);
287: } finally {
288: ps.close();
289: out.close();
290: }
291: }
292: }
293:
294: /**
295: * Executes sql script passed in "sql" parameter. Query results are sent to stylesheet.
296: *
297: * @param request
298: * @param response
299: * @param servlet
300: * @return
301: * @throws IOException
302: * @throws IOException
303: */
304: public Object sql(HttpServletRequest request,
305: HttpServletResponse response, ActionServlet servlet)
306: throws IOException {
307: String sqlProcessor = request.getParameter("processor");
308: if (sqlProcessor == null) {
309: return "SQL Processor is not set";
310: }
311:
312: RequestContext context = new RequestContext(request);
313: Object object = context.get(sqlProcessor);
314: if (object == null) {
315: return "SQL Processor not found: " + sqlProcessor;
316: }
317:
318: if (!(object instanceof SQLProcessor)) {
319: return "Not an instance of SQLProcessor: " + sqlProcessor;
320: }
321:
322: SQLProcessor processor = (SQLProcessor) object;
323:
324: String sql = request.getParameter("sql");
325: if (sql == null) {
326: return "Nothing to execute";
327: }
328: String type = request.getParameter("type");
329: try {
330: if ("Query".equals(type)) {
331: final Map ret = new HashMap();
332: final Collection data = new ArrayList();
333: final Collection meta = new ArrayList();
334: ret.put("data", data);
335: processor.processSelect(sql, null,
336: new MetadataAwareRowProcessor() {
337: int columnCount;
338:
339: public void processMetadata(
340: ResultSetMetaData rsmd)
341: throws SQLException {
342: columnCount = rsmd.getColumnCount();
343: for (int i = 1; i <= columnCount; ++i) {
344: meta.add(rsmd.getColumnName(i));
345: }
346: ret.put("meta", meta);
347: }
348:
349: public void onEmptyResultSet()
350: throws SQLException {
351: // Nothing
352:
353: }
354:
355: public boolean process(ResultSet rs)
356: throws SQLException {
357: Collection row = new ArrayList();
358:
359: Iterator it = meta.iterator();
360: while (it.hasNext()) {
361: final String columnName = (String) it
362: .next();
363: final String value = rs
364: .getString(columnName);
365: row.add(new DomSerializable() {
366:
367: public void toDom(Element holder) {
368: holder.setAttribute("name",
369: columnName);
370: if (isBlank(value)) {
371: holder.setAttribute(
372: "blank", "yes");
373: } else {
374: holder
375: .appendChild(holder
376: .getOwnerDocument()
377: .createTextNode(
378: value));
379: }
380: }
381:
382: });
383: }
384: data.add(row);
385: return true;
386: }
387:
388: });
389: return ret;
390: } else if ("Update".equals(type)) {
391: return "Updates: " + processor.processUpdate(sql, null);
392: } else if ("Script".equals(type)) {
393: processor.executeScript(new StringReader(sql));
394: return "Done";
395: } else {
396: return "Wrong command type: " + type;
397: }
398: } catch (SQLException e) {
399: StringWriter sw = new StringWriter();
400: PrintWriter pw = new PrintWriter(sw);
401: Throwable rootCause = e;
402: while (rootCause.getCause() != null
403: && rootCause.getCause() != rootCause) {
404: rootCause = rootCause.getCause();
405: }
406: if (!isBlank(rootCause.getMessage())) {
407: pw.write("<PRE style=\"color:red\">");
408: pw.write(rootCause.getMessage());
409: pw.write("</PRE><HR/>");
410: }
411: pw.write("<PRE style=\"color:red\">");
412: e.printStackTrace(pw);
413: pw.write("</PRE>");
414: pw.close();
415: sw.close();
416: return sw.toString();
417: }
418: }
419:
420: protected Menu getMatched(HttpServletRequest request) {
421: Menu ret = (Menu) request
422: .getAttribute(MenuFilter.MENU_MATCHED_ATTRIBUTE);
423: if (ret == null) {
424: ret = (Menu) request.getSession().getAttribute(
425: MenuFilter.MENU_MATCHED_ATTRIBUTE);
426: }
427: return ret;
428: }
429:
430: protected Menu getMenu(HttpServletRequest request) {
431: Menu ret = (Menu) request.getAttribute(Menu.class.getName());
432: if (ret == null) {
433: HttpSession session = request.getSession(false);
434: if (session != null) {
435: ret = (Menu) session.getAttribute(Menu.class.getName());
436: }
437: }
438: return ret;
439: }
440:
441: /**
442: * Returns object for remote method invocation. Default implementation delegates to request context.
443: * @param request
444: * @param response
445: * @param name
446: * @return
447: */
448: protected Object getRemoted(HttpServletRequest request,
449: HttpServletResponse response, String name) {
450: return new RequestContext(request).get(name);
451:
452: }
453:
454: /**
455: * Method for remote invocations over HTTP
456: * @param request
457: * @param response
458: * @throws IOException
459: */
460: public void invoke(final HttpServletRequest request,
461: final HttpServletResponse response) throws IOException {
462: Registry registry = new Registry() {
463:
464: public Object get(String name) {
465: return getRemoted(request, response, name);
466: }
467:
468: };
469:
470: registry.invoke(request.getInputStream(), response
471: .getOutputStream());
472: }
473: }
|