001: /*
002: * Copyright (C) 2004 TiongHiang Lee
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * Email: thlee@onemindsoft.org
019: */
020:
021: package org.onemind.swingweb.servlet;
022:
023: import java.io.*;
024: import java.net.URLEncoder;
025: import java.util.*;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028: import javax.servlet.ServletConfig;
029: import javax.servlet.ServletException;
030: import javax.servlet.http.*;
031: import org.apache.commons.fileupload.DiskFileUpload;
032: import org.onemind.awtbridge.BridgeContextSession;
033: import org.onemind.awtbridge.session.SessionLifecycleListener;
034: import org.onemind.commons.java.util.*;
035: import org.onemind.commons.java.xml.digest.SaxDigesterHandler;
036: import org.onemind.jxp.*;
037: import org.onemind.swingweb.*;
038: import org.onemind.swingweb.resource.Resource;
039: import org.onemind.swingweb.session.SwingWebSession;
040: import org.onemind.swingweb.session.URLLocal;
041: import org.onemind.swingweb.util.ComponentOptions;
042:
043: /**
044: * A Abstract Servlet for initializing the swingweb application. There's only two parameter needed
045: * for the application: <br>
046: * <ul>
047: * <li>app-class - the application class </li>
048: * <li>swingweb-config - the swing web configuration </li>
049: * </ul>
050: *
051: * @author TiongHiang Lee (thlee@onemindsoft.org)
052: *
053: */
054: public abstract class AbstractSwingWebServlet extends HttpServlet
055: implements SessionLifecycleListener {
056:
057: /** the logger * */
058: private static final Logger _logger = Logger
059: .getLogger(AbstractSwingWebServlet.class.getName());
060:
061: public static final String KEY_SWCONTEXTS = "SwingWebContexts";
062:
063: private static final String KEY_APP_CLASS = "app-class";
064:
065: private static final String KEY_CONFIG = "swingweb-config";
066:
067: private static final String KEY_ENCODING = "char-encoding";
068:
069: private static final String KEY_GET_AFTER_POST = "get-after-post";
070:
071: private static final String KEY_TEMPLATE_CONTENT = "content-template";
072:
073: private static final String KEY_TEMPLATE_SESSION_ENDED = "session-ended-template";
074:
075: private static final String KEY_COMPONENT_OPTIONS = "component-options-config";
076:
077: /** the application to start **/
078: private String _appClass;
079:
080: /** the session ended message **/
081: private String _sessionEndedMessage;
082:
083: /** redirect to get after post **/
084: private boolean _getAfterPost = true;
085:
086: /** the upload **/
087: private DiskFileUpload _uploadRepository;
088:
089: /** the swing web manager **/
090: private SwingWebAppManager _manager;
091:
092: /** the encoding **/
093: private String _charEncoding;
094:
095: /** the sessions **/
096: private WeakHashMap _sessions = new WeakHashMap();
097:
098: /** the jxp processor **/
099: private JxpProcessor _processor;
100:
101: /** the context template **/
102: private String _contentTemplate;
103:
104: /** the session end template **/
105: private String _sessionEndTemplate;
106:
107: /** the component options **/
108: private ComponentOptions _comOpts;
109:
110: /** the component options file **/
111: private File _comOptsFile;
112:
113: /** a callback to pass to the template for rendering **/
114: public interface CallBack {
115:
116: public void call() throws Exception;
117: }
118:
119: class RenderCallBack implements CallBack {
120:
121: Writer _writer;
122:
123: public RenderCallBack(Writer writer) {
124: _writer = writer;
125: }
126:
127: public void call() throws Exception {
128: _manager.renderOutput(_writer);
129: }
130: };
131:
132: class InputRenderCallBack extends RenderCallBack {
133:
134: Map _form;
135:
136: public InputRenderCallBack(Writer writer, Map form) {
137: super (writer);
138: _form = form;
139: }
140:
141: public void call() throws Exception {
142: _manager.handleInput(_form);
143: _manager.renderOutput(_writer);
144: }
145: };
146:
147: /**
148: * {@inheritDoc}
149: */
150: protected void doGet(HttpServletRequest req,
151: HttpServletResponse resp) throws ServletException,
152: IOException {
153: doPost(req, resp);
154: }
155:
156: public String getConfig(ServletConfig config, String key, String def) {
157: String value = config.getInitParameter(key);
158: if (value == null) {
159: value = def;
160: }
161: return value;
162: }
163:
164: /**
165: * {@inheritDoc}
166: */
167: public void init(ServletConfig config) throws ServletException {
168: super .init(config);
169: //make it run swingweb
170: System.getProperties().put("awt.toolkit",
171: "org.onemind.swingweb.SwingWebToolkit");
172: LogUtils.initLoggingFromClassPath();
173: _appClass = config.getInitParameter(KEY_APP_CLASS);
174: String swConfig = config.getInitParameter(KEY_CONFIG);
175: _charEncoding = getConfig(config, KEY_ENCODING, "UTF-8");
176: _getAfterPost = ObjectUtils.toBool(config
177: .getInitParameter(KEY_GET_AFTER_POST), true);
178: // create a new file upload handler
179: _uploadRepository = new DiskFileUpload();
180: // set upload parameters
181: _uploadRepository.setRepositoryPath(System
182: .getProperty("java.io.tmpdir")); //TODO: make become configurable setting
183: //_uploadRepository.setSizeThreshold(1000000); //file > 1 meg write to disk
184: _uploadRepository.setSizeThreshold(0); //write any file to disk
185: _uploadRepository.setSizeMax(-1); //no maximum size
186: _comOpts = loadComponentOptions(config);
187: try {
188: _manager = new SwingWebAppManager(new FileInputStream(
189: getServletContext().getRealPath(swConfig)));
190: synchronized (KEY_SWCONTEXTS) {
191: Set contextSet = (Set) getServletContext()
192: .getAttribute(KEY_SWCONTEXTS);
193: if (contextSet == null) {
194: contextSet = new HashSet();
195: getServletContext().setAttribute(KEY_SWCONTEXTS,
196: contextSet);
197: }
198: contextSet.add(_manager.getContext());
199: }
200: _contentTemplate = getConfig(config, KEY_TEMPLATE_CONTENT,
201: "/org/onemind/swingweb/servlet/swingweb-app-content.jxp");
202: _sessionEndTemplate = getConfig(config,
203: KEY_TEMPLATE_SESSION_ENDED,
204: "/org/onemind/swingweb/servlet/swingweb-app-session-ended.jxp");
205: } catch (Exception e) {
206: throw new ServletException("Cannot init swingweb servlet",
207: e);
208: }
209: }
210:
211: private ComponentOptions loadComponentOptions(ServletConfig config)
212: throws ServletException {
213: String configFile = config
214: .getInitParameter(KEY_COMPONENT_OPTIONS);
215: if (configFile == null) {
216: throw new ServletException(KEY_COMPONENT_OPTIONS
217: + " must be configured");
218: }
219: File comOptsFile = new File(getServletContext().getRealPath(
220: configFile));
221: if (comOptsFile.exists()) {
222: try {
223: SaxDigesterHandler handler = new SaxDigesterHandler();
224: ComponentOptions comOpts = new ComponentOptions();
225: handler.addDigester(comOpts);
226: handler.parse(new FileInputStream(comOptsFile));
227: _comOptsFile = comOptsFile;
228: return comOpts;
229: } catch (Exception ex) {
230: throw new ServletException("Unable to initialize "
231: + KEY_COMPONENT_OPTIONS
232: + comOptsFile.getAbsolutePath(), ex);
233: }
234: } else {
235: throw new ServletException(KEY_COMPONENT_OPTIONS
236: + comOptsFile.getAbsolutePath() + " does not exist");
237: }
238: }
239:
240: /**
241: * Get the processor
242: * @param req the request
243: * @return the processor
244: * @throws Exception
245: */
246: private JxpProcessor getProcessor(HttpServletRequest req) {
247: if (_processor == null) {
248: synchronized (this ) {
249: MultiSourcePageSource multisource = new MultiSourcePageSource();
250: FilePageSource filesource = new FilePageSource(req
251: .getRealPath("/"), _charEncoding);
252: filesource.setModCheck(true);
253: filesource.setCaching(true);
254: multisource.addPageSource(filesource);
255: ResourceStreamPageSource streamsource = new ResourceStreamPageSource(
256: "/");
257: multisource.addPageSource(streamsource);
258: _processor = new JxpProcessor(new JxpContext(
259: multisource));
260: }
261: }
262: return _processor;
263: }
264:
265: /**
266: * {@inheritDoc}
267: */
268: protected void doPost(HttpServletRequest req,
269: HttpServletResponse resp) throws ServletException,
270: IOException {
271: resp.setContentType("text/html; charset=" + _charEncoding);
272: resp.setStatus(200, "OK");
273: if (_appClass == null) {
274: PrintWriter writer = resp.getWriter();
275: writer.write("No application is configured at this url");
276: writer.flush();
277: writer.close();
278: } else {
279: //TODO: decide whether this environment need to be passed on
280: Map env = new HashMap();
281: env.putAll(ServletUtils
282: .getServletEnvironment(getServletConfig()));
283: env.putAll(ServletUtils.getRequestEnvironment(req));
284: env.putAll(ServletUtils.getExtraRequestEnvironment(req));
285: PrintWriter writer = null;
286: try {
287: Map form = ServletUtils.getRequestParameters(req,
288: _uploadRepository);
289: if (form.containsKey("swresource")) {
290: sendResource(req, resp, (String) form
291: .get("swresource"));
292: } else {
293: String uri = req.getRequestURI();
294: HttpSession session = req.getSession(true);
295: writer = resp.getWriter();
296: Object obj = session.getValue(uri);
297: if (obj instanceof Boolean) {//session ended
298: renderWithTemplate(req, null,
299: _sessionEndTemplate, form, writer, null);
300: session.removeAttribute(uri);
301: } else {
302: try {
303: SwingWebSession swSession = (SwingWebSession) obj;
304: if (swSession == null) {
305: swSession = (SwingWebSession) createSession(
306: _manager.getContext(), session);
307: swSession.addListener(this );
308: swSession.putValue(
309: "SWINGWEB_SESSION_ID",
310: String.valueOf(System
311: .currentTimeMillis()));
312: session.setAttribute(uri, swSession);
313: swSession.putAllValues(env);
314: swSession.putValue("HTTP_FORM", form);
315: swSession.putValue("HTTP_SESSION",
316: session);
317: swSession.putValue(
318: "COMPONENT_OPTIONS_CONFIG",
319: _comOptsFile.getAbsolutePath());
320: swSession.putValue("COMPONENT_OPTIONS",
321: _comOpts);
322: _sessions.put(swSession, session);
323: _manager.setCurrentSession(swSession);
324: startApp(swSession, req, resp);
325: } else {
326: swSession.putAllValues(env);
327: swSession.putValue(
328: "COMPONENT_OPTIONS_CONFIG",
329: _comOptsFile.getAbsolutePath());
330: swSession.putValue("COMPONENT_OPTIONS",
331: _comOpts);
332: swSession.putValue("HTTP_FORM", form);
333: swSession.putValue("HTTP_SESSION",
334: session);
335: _manager.setCurrentSession(swSession);
336: }
337: if (_getAfterPost) {
338: if (req.getMethod().equalsIgnoreCase(
339: "GET")) {
340: assertURLLocals(swSession, req);
341: renderWithTemplate(req, swSession,
342: _contentTemplate, form,
343: writer, new RenderCallBack(
344: writer));
345: } else {
346: _manager.handleInput(form);
347: StringBuffer url = new StringBuffer(
348: uri);
349: String queryString = constructURLFromURLLocals(
350: swSession, req);
351: if (queryString.length() > 0) {
352: url.append("?");
353: url.append(queryString);
354: }
355: // url.append("&");
356: // url.append(System.currentTimeMillis());
357: resp.sendRedirect(url.toString());
358: }
359: } else {
360: renderWithTemplate(req, swSession,
361: _contentTemplate, form, writer,
362: new InputRenderCallBack(writer,
363: form));
364: }
365: } finally {
366: _manager.setCurrentSession(null);
367: }
368: }
369: }
370: } catch (IOException e) {
371: _logger.severe(LogUtils.getTrace(e));
372: if (writer != null) {
373: e.printStackTrace(writer);
374: }
375: } catch (Exception e) {
376: //e.printStackTrace(writer);
377: _logger.severe(LogUtils.getTrace(e));
378: if (writer != null) {
379: e.printStackTrace(writer);
380: }
381: }
382: if (writer != null) {
383: writer.flush();
384: }
385: resp.flushBuffer();
386: if (_logger.isLoggable(Level.FINEST)) {
387: _logger.finest("Done handling request");
388: }
389: }
390: }
391:
392: private void renderWithTemplate(HttpServletRequest req,
393: SwingWebSession swSession, String templateName, Map form,
394: Writer writer, CallBack callBack) throws Exception {
395: JxpProcessor processor = getProcessor(req);
396: Map env = new HashMap();
397: env.put("request", req);
398: env.put("writer", writer);
399: env.put("form", form);
400: env.put("callBack", callBack);
401: env.put("swSession", swSession);
402: env.put("session", req.getSession());
403: processor.process(templateName, writer, env);
404: }
405:
406: protected void generateEnvironment() {
407: }
408:
409: protected abstract SwingWebSession createSession(
410: SwingWebContext context, HttpSession session);
411:
412: protected abstract void startApp(SwingWebSession swSession,
413: HttpServletRequest req, HttpServletResponse resp)
414: throws Exception;
415:
416: private String constructURLFromURLLocals(SwingWebSession swSession,
417: HttpServletRequest req) {
418: StringBuffer sb = new StringBuffer();
419: SwingWebComponentManager man = (SwingWebComponentManager) swSession
420: .getComponentManager();
421: List urlLocals = man.getURLLocals();
422: Iterator it = urlLocals.iterator();
423: boolean isFirst = true;
424: while (it.hasNext()) {
425: URLLocal local = (URLLocal) it.next();
426: String value = local.getSetValue();
427: if (value != null) {
428: if (isFirst) {
429: isFirst = false;
430: } else {
431: sb.append("&");
432: }
433: sb.append(URLEncoder.encode(local.getName()));
434: sb.append("=");
435: sb.append(URLEncoder.encode(value));
436: } else if (local.isAlwaysShow()) {
437: if (isFirst) {
438: isFirst = false;
439: } else {
440: sb.append("&");
441: }
442: sb.append(URLEncoder.encode(local.getName()));
443: sb.append("=");
444: sb.append(URLEncoder.encode((String) local.getValue()));
445: }
446: }
447: return sb.toString();
448: }
449:
450: private static void assertURLLocals(SwingWebSession swSession,
451: HttpServletRequest req) {
452: Map param = req.getParameterMap();
453: SwingWebComponentManager man = (SwingWebComponentManager) swSession
454: .getComponentManager();
455: List urlLocals = man.getURLLocals();
456: Iterator it = urlLocals.iterator();
457: while (it.hasNext()) {
458: URLLocal local = (URLLocal) it.next();
459: Object value = param.get(local.getName());
460: if (value instanceof String[]) {
461: value = ((String[]) value)[0];
462: }
463: String paramValue = (String) value;
464: local.setValue(paramValue);
465: }
466: }
467:
468: /**
469: * Send the resource
470: * @param resp
471: * @param object
472: */
473: private void sendResource(HttpServletRequest req,
474: HttpServletResponse resp, String id) {
475: Resource res = _manager.getContext().getResource(id);
476: if (res != null) {
477: try {
478: resp.setContentType(res.getType());
479: resp.setStatus(200, "Ok");
480: OutputStream out = new BufferedOutputStream(resp
481: .getOutputStream());
482: FileUtils.copyStream(res.getInputStream(), out, 1024);
483: out.flush();
484: } catch (Exception e) {
485: _logger.throwing(getClass().getName(), "sendResource",
486: e);
487: }
488: } else {
489: resp.setContentType("text/html");
490: resp.setStatus(404, "Resource not found");
491: }
492: }
493:
494: public final SwingWebAppManager getManager() {
495: return _manager;
496: }
497:
498: public final String getAppClass() {
499: return _appClass;
500: }
501:
502: public void sessionDestroyed(HttpSessionEvent evt) {
503: //close up swingweb sessions when httpsession ended
504: if (_manager != null) {
505: HttpSession session = evt.getSession();
506: Enumeration names = session.getAttributeNames();
507: while (names.hasMoreElements()) {
508: String name = (String) names.nextElement();
509: Object value = session.getAttribute(name);
510: if (value instanceof SwingWebSession) {
511: SwingWebSession swSession = ((SwingWebSession) value);
512: _manager.setCurrentSession(swSession);
513: try {
514: swSession.close();
515: } finally {
516: _manager.setCurrentSession(null);
517: }
518: }
519: }
520: }
521: }
522:
523: public void sessionEnded(BridgeContextSession session) {
524: //This will be called by Session.close()
525: HttpSession httpSession = (HttpSession) _sessions
526: .remove(session);
527: if (httpSession != null) {
528: Enumeration names = httpSession.getAttributeNames();
529: while (names.hasMoreElements()) {
530: String name = (String) names.nextElement();
531: if (httpSession.getAttribute(name) == session) {
532: httpSession.removeAttribute(name);
533: httpSession.setAttribute(name, Boolean.FALSE);
534: }
535: }
536: }
537: }
538: }
|