001: // Copyright 2005 Chibacon
002: /*
003: *
004: * Artistic License
005: *
006: * Preamble
007: *
008: * The intent of this document is to state the conditions under which a Package may be copied, such that
009: * the Copyright Holder maintains some semblance of artistic control over the development of the
010: * package, while giving the users of the package the right to use and distribute the Package in a
011: * more-or-less customary fashion, plus the right to make reasonable modifications.
012: *
013: * Definitions:
014: *
015: * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives
016: * of that collection of files created through textual modification.
017: *
018: * "Standard Version" refers to such a Package if it has not been modified, or has been modified
019: * in accordance with the wishes of the Copyright Holder.
020: *
021: * "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
022: *
023: * "You" is you, if you're thinking about copying or distributing this Package.
024: *
025: * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication
026: * charges, time of people involved, and so on. (You will not be required to justify it to the
027: * Copyright Holder, but only to the computing community at large as a market that must bear the
028: * fee.)
029: *
030: * "Freely Available" means that no fee is charged for the item itself, though there may be fees
031: * involved in handling the item. It also means that recipients of the item may redistribute it under
032: * the same conditions they received it.
033: *
034: * 1. You may make and give away verbatim copies of the source form of the Standard Version of this
035: * Package without restriction, provided that you duplicate all of the original copyright notices and
036: * associated disclaimers.
037: *
038: * 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain
039: * or from the Copyright Holder. A Package modified in such a way shall still be considered the
040: * Standard Version.
041: *
042: * 3. You may otherwise modify your copy of this Package in any way, provided that you insert a
043: * prominent notice in each changed file stating how and when you changed that file, and provided that
044: * you do at least ONE of the following:
045: *
046: * a) place your modifications in the Public Domain or otherwise make them Freely
047: * Available, such as by posting said modifications to Usenet or an equivalent medium, or
048: * placing the modifications on a major archive site such as ftp.uu.net, or by allowing the
049: * Copyright Holder to include your modifications in the Standard Version of the Package.
050: *
051: * b) use the modified Package only within your corporation or organization.
052: *
053: * c) rename any non-standard executables so the names do not conflict with standard
054: * executables, which must also be provided, and provide a separate manual page for each
055: * non-standard executable that clearly documents how it differs from the Standard
056: * Version.
057: *
058: * d) make other distribution arrangements with the Copyright Holder.
059: *
060: * 4. You may distribute the programs of this Package in object code or executable form, provided that
061: * you do at least ONE of the following:
062: *
063: * a) distribute a Standard Version of the executables and library files, together with
064: * instructions (in the manual page or equivalent) on where to get the Standard Version.
065: *
066: * b) accompany the distribution with the machine-readable source of the Package with
067: * your modifications.
068: *
069: * c) accompany any non-standard executables with their corresponding Standard Version
070: * executables, giving the non-standard executables non-standard names, and clearly
071: * documenting the differences in manual pages (or equivalent), together with instructions
072: * on where to get the Standard Version.
073: *
074: * d) make other distribution arrangements with the Copyright Holder.
075: *
076: * 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge
077: * any fee you choose for support of this Package. You may not charge a fee for this Package itself.
078: * However, you may distribute this Package in aggregate with other (possibly commercial) programs as
079: * part of a larger (possibly commercial) software distribution provided that you do not advertise this
080: * Package as a product of your own.
081: *
082: * 6. The scripts and library files supplied as input to or produced as output from the programs of this
083: * Package do not automatically fall under the copyright of this Package, but belong to whomever
084: * generated them, and may be sold commercially, and may be aggregated with this Package.
085: *
086: * 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of
087: * this Package.
088: *
089: * 8. The name of the Copyright Holder may not be used to endorse or promote products derived from
090: * this software without specific prior written permission.
091: *
092: * 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
093: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
094: * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
095: *
096: */
097: package mc.formgenerator.servlets;
098:
099: import org.apache.log4j.Category;
100: import org.chiba.adapter.AbstractChibaAdapter;
101: import org.chiba.adapter.InteractionHandler;
102: import org.chiba.adapter.ChibaEvent;
103: import org.chiba.tools.xslt.StylesheetLoader;
104: import org.chiba.tools.xslt.UIGenerator;
105: import org.chiba.tools.xslt.XSLTGenerator;
106: import org.chiba.xml.xforms.ChibaBean;
107: import org.chiba.xml.xforms.config.Config;
108: import org.chiba.xml.xforms.exception.XFormsException;
109: import org.w3c.dom.Node;
110:
111: import java.io.Writer;
112: import java.net.URISyntaxException;
113: import java.util.HashMap;
114: import java.util.Map;
115:
116: /**
117: * integrates XForms Processor into Web-applications and handles request processing. This is the default
118: * implementation of ChibaAdapter and besides handling the interaction it also
119: * manages a UIGenerator to build the rendered output for the browser.
120: *
121: * @author joern turner
122: * @version $Id$
123: */
124: public class ServletAdapter extends AbstractChibaAdapter {
125:
126: private static final Category LOGGER = Category
127: .getInstance(ServletAdapter.class);
128: public static final String HTTP_SERVLET_REQUEST = "chiba.web.request";
129: public static final String HTTP_SESSION_OBJECT = "chiba.web.session";
130: public static final String HTTP_UPLOAD_DIR = "chiba.web.uploadDir";
131:
132: private ChibaBean chibaBean = null;
133: private String formURI = null;
134: private String actionUrl = null;
135: private String CSSFile = null;
136: private String stylesheet = null;
137: private String contextRoot = null;
138: private UIGenerator generator = null;
139: private String stylesheetPath = null;
140: private HashMap context = null;
141: public static final String USERAGENT = "chiba.useragent";
142: private InteractionHandler handler;
143: private Node formNode;
144: //06-07-2005 host URL to use in ui.xsl
145: private String hostUrl;
146:
147: /**
148: * Creates a new ServletAdapter object.
149: */
150: public ServletAdapter() {
151: this .chibaBean = createXFormsProcessor();
152: this .context = new HashMap();
153: chibaBean.setContext(this .context);
154:
155: }
156:
157: /**
158: * creates an instance of ChibaBean, configures it and creates a generator instance
159: *
160: * @throws XFormsException If an error occurs
161: */
162: public void init() throws XFormsException {
163:
164: if (this .formURI != null) {
165: //07-07-2005 we''l allways work with Documents (not with Files)
166: /*
167: //this.chibaBean.setXMLContainer(this.formURI);
168: try {
169: setXForms(new URI(this.formURI));
170: } catch (URISyntaxException e) {
171: throw new XFormsException("URI not well-formed",e);
172: }
173: this.chibaBean.setBaseURI(this.formURI.toString());
174: */
175: this .chibaBean.setBaseURI(this .formURI.toString());
176:
177: /*
178: }else if(formNode != null){
179: // todo: base uri should be set to some default - the forms dir ? or leave to the developer?
180: // this.chibaBean.setBaseURI(...);
181: setXForms(this.formNode);
182: }
183: */
184: } else {
185: throw new XFormsException("No base URI specified");
186: }
187: if (LOGGER.isDebugEnabled()) {
188: LOGGER.debug(this .toString());
189: LOGGER.debug("Form URI: " + formURI);
190: LOGGER.debug("CSS-File: " + CSSFile);
191: LOGGER.debug("XSLT stylesheet: " + stylesheet);
192: LOGGER.debug("action URL: " + actionUrl);
193: }
194:
195: this .chibaBean.init();
196: this .handler = getNewInteractionHandler();
197: generator = createUIGenerator();
198: }
199:
200: /**
201: * uses an URI in string representation to point to the XForms document to process. This URI always comes
202: * as a http Url in the context of servlet processing.
203: *
204: * @param uriString a http URI pointing to the requested XForms
205: * @throws URISyntaxException thrown in case an invalid http Url is passed in
206: * @throws XFormsException thrown in case the processor couldn't be initialized from given URI, mostly likely due
207: * to a problem in the form.
208: */
209: public void setXFormsURI(String uriString)
210: throws URISyntaxException, XFormsException {
211: this .formURI = uriString;
212: }
213:
214: /**
215: * return a new InteractionHandler.
216: *
217: * This method returns a new HttpRequestHandler.
218: *
219: * @return returns a new
220: */
221: protected InteractionHandler getNewInteractionHandler()
222: throws XFormsException {
223: return new HttpRequestHandler(this .chibaBean);
224: }
225:
226: /**
227: * ServletAdapter is special in its event handling - it simply ignores the concrete UIEvent
228: * and applies all request params and the triggerd action in one batch process.
229: *
230: * @param event ignored
231: * @throws XFormsException
232: */
233: public void dispatch(ChibaEvent event) throws XFormsException {
234: this .handler.execute();
235: }
236:
237: /**
238: * terminates the XForms processing. right place to do cleanup of resources.
239: *
240: * @throws org.chiba.xml.xforms.exception.XFormsException
241: *
242: */
243: public void shutdown() throws XFormsException {
244: this .chibaBean.shutdown();
245: }
246:
247: /**
248: * Instructs the application environment to forward the given response.
249: *
250: * @param response a map containing at least a response stream and optional
251: * header information.
252: */
253: public void forward(Map response) {
254: this .chibaBean.getContext().put(SUBMISSION_RESPONSE, response);
255: }
256:
257: /**
258: * returns a Map object containing a forward uri. this is used by the 'load' action
259: *
260: * @return a Map object containing a forward uri
261: */
262: public Map getForwardMap() {
263: return (Map) chibaBean.getContext().get(SUBMISSION_RESPONSE);
264: }
265:
266: /**
267: * generates the user interface.
268: *
269: * This method generates the user interface.
270: *
271: * @throws XFormsException
272: */
273: public final void buildUI() throws XFormsException {
274: String dataPrefix = Config.getInstance().getProperty(
275: "chiba.web.dataPrefix");
276: String triggerPrefix = Config.getInstance().getProperty(
277: "chiba.web.triggerPrefix");
278: String userAgent = (String) getContextProperty(ServletAdapter.USERAGENT);
279:
280: generator.setParameter("data-prefix", dataPrefix);
281: generator.setParameter("trigger-prefix", triggerPrefix);
282: generator.setParameter("user-agent", userAgent);
283: if (CSSFile != null) {
284: generator.setParameter("css-file", CSSFile);
285: }
286:
287: if (LOGGER.isDebugEnabled()) {
288: LOGGER.debug(">>> setting UI generator params...");
289: LOGGER.debug("data-prefix=" + dataPrefix);
290: LOGGER.debug("trigger-prefix=" + triggerPrefix);
291: LOGGER.debug("user-agent=" + userAgent);
292: if (CSSFile != null) {
293: LOGGER.debug("css-file=" + CSSFile);
294: }
295: LOGGER.debug(">>> setting UI generator params...end");
296: }
297:
298: generator.setInputNode(this .chibaBean.getXMLContainer());
299: generator.generate();
300: }
301:
302: /**
303: * generates the user interface.
304: *
305: * This conveniance method generates the user interface
306: * using a java.io.Writer.
307: *
308: * @param responseWriter the Writer to use for the result stream
309: * @throws XFormsException
310: */
311: public void buildUI(Writer responseWriter) throws XFormsException {
312: generator.setOutput(responseWriter);
313: this .buildUI();
314: }
315:
316: /**
317: * factory method for creating UIGenerator instances.
318: *
319: * @return the created UIGenerator instance
320: * @throws XFormsException
321: */
322: public UIGenerator createUIGenerator() throws XFormsException {
323: //create and configure StylesheetLoader
324: StylesheetLoader stylesLoader = new StylesheetLoader(
325: stylesheetPath);
326:
327: //if there's a stylesheet specified in the request
328: if (stylesheet != null) {
329: stylesLoader.setStylesheetFile(stylesheet);
330: }
331:
332: if (generator == null) {
333: generator = getNewUIGenerator(stylesLoader);
334: }
335: //todo: move these params to buildUI too
336: generator.setParameter("action-url", actionUrl);
337: generator.setParameter("host-url", hostUrl);
338: //generator.setParameter("debug-enabled", String.valueOf(LOGGER.isDebugEnabled()));
339: generator.setParameter("debug-enabled", String.valueOf(false));
340: String selectorPrefix = Config.getInstance().getProperty(
341: HttpRequestHandler.SELECTOR_PREFIX_PROPERTY,
342: HttpRequestHandler.SELECTOR_PREFIX_DEFAULT);
343: generator.setParameter("selector-prefix", selectorPrefix);
344: String removeUploadPrefix = Config.getInstance().getProperty(
345: HttpRequestHandler.REMOVE_UPLOAD_PREFIX_PROPERTY,
346: HttpRequestHandler.REMOVE_UPLOAD_PREFIX_DEFAULT);
347: generator.setParameter("remove-upload-prefix",
348: removeUploadPrefix);
349: if (CSSFile != null) {
350: generator.setParameter("css-file", CSSFile);
351: }
352: return generator;
353: }
354:
355: /**
356: * return a new UIGenerator.
357: *
358: * This method returns a new XSLTGenerator.
359: *
360: * @param stylesLoader
361: * @return returns a new UIGenerator object
362: */
363: protected UIGenerator getNewUIGenerator(
364: StylesheetLoader stylesLoader) throws XFormsException {
365: return new XSLTGenerator(stylesLoader);
366: }
367:
368: /**
369: * Instructs the application environment to setRedirect to the given URI.
370: *
371: * @param uri an absolute URI.
372: */
373: public void setRedirect(String uri) {
374: chibaBean.getContext().put(LOAD_URI, uri);
375: }
376:
377: /**
378: * returns the redirect Uri
379: *
380: * @return the redirect Uri
381: */
382: public String getRedirectUri() {
383: return (String) chibaBean.getContext().get(LOAD_URI);
384: }
385:
386: // ************************* ACCESSORS ********************************************
387:
388: /**
389: * returns the ChibaBean instance used with this servletAdapter
390: *
391: * @return the ChibaBean instance used with this servletAdapter
392: */
393: public ChibaBean getChibaBean() {
394: return chibaBean;
395: }
396:
397: /**
398: * sets the Url for the action target
399: *
400: * @param actionUrl the Url for the action target
401: */
402: public void setActionUrl(String actionUrl) {
403: this .actionUrl = actionUrl;
404: }
405:
406: public String getContextRoot() {
407: return contextRoot;
408: }
409:
410: /**
411: * sets the context root for the webapp. This is used to build the correct pathes of relative path-statements
412: *
413: * @param contextRoot the root of the webapp
414: */
415: public void setContextRoot(String contextRoot) {
416: this .contextRoot = contextRoot;
417: }
418:
419: public String getHostUrl() {
420: return hostUrl;
421: }
422:
423: /**
424: * sets host URL.
425: *
426: * @param hostUrl the URL of the host
427: */
428: public void setHostUrl(String hostUrl) {
429: this .hostUrl = hostUrl;
430: }
431:
432: /**
433: * sets the path where to find XForms documents.
434: *
435: * @param formPath the path where to find XForms documents
436: */
437: /*
438: public void setFormPath(String formPath) {
439: this.formPath = formPath;
440: this.formURI = null;
441: this.formNode = null;
442: }
443: */
444:
445: /*
446: public void setFormURI(URI formURI) {
447: this.formURI = formURI;
448: }
449: */
450:
451: /**
452: * sets a XForms host document for processing by directly passing a DOM Node.
453: *
454: * @param formNode the rootnode of the host document
455: * @deprecated use setXForms(Node node) from ChibaAdapter instead
456: */
457: public void setFormDocument(Node formNode) {
458: this .formNode = formNode;
459: }
460:
461: /**
462: * gets a context property from Chiba's context hashmap.
463: *
464: * @param key
465: * @return a context property from Chiba's context hashmap.
466: */
467: public Object getContextProperty(String key) {
468: return context.get(key);
469: }
470:
471: /**
472: * stores a context property into Chiba's context hashmap.
473: *
474: * @param key the key to associate with val
475: * @param val the value object to store
476: */
477: public void setContextProperty(String key, Object val) {
478: context.put(key, val);
479: }
480:
481: public String getUploadDir() {
482: return (String) getContextProperty(HTTP_UPLOAD_DIR);
483: }
484:
485: /**
486: * sets the directory where uploaded files are stored.
487: *
488: * @param uploadDir the directory where uploaded files are stored
489: */
490: public void setUploadDir(String uploadDir) {
491: setContextProperty(HTTP_UPLOAD_DIR, uploadDir);
492: }
493:
494: /**
495: * sets the path where to find the xslt stylesheets
496: *
497: * @param stylesPath the path where to find the xslt stylesheets
498: */
499: public void setStylesheetPath(String stylesPath) {
500: this .stylesheetPath = stylesPath;
501: }
502:
503: /**
504: * set the CSS file to use for styling the user interface
505: *
506: * @param css the CSS file to use for styling the user interface
507: */
508: public void setCSS(String css) {
509: this .CSSFile = css;
510: }
511:
512: /**
513: * sets the name of the xslt stylesheet to use for building the UI
514: *
515: * @param stylesheetFile the name of the xslt stylesheet to use for building the UI
516: */
517: public void setStylesheet(String stylesheetFile) {
518: this .stylesheet = stylesheetFile;
519: }
520:
521: /**
522: * build the absolute path to the requested file and test its
523: * existence. <br><br>
524: *
525: * @param uri - the relative uri of the file
526: * @return returns the absolute path to the file
527: */
528: /*
529: private String locateFile(String uri) throws XFormsException {
530: if (uri == null) {
531: throw new XFormsException("No form file specified");
532: }
533:
534: //construct absolute path to file and check existence
535: String filePath = contextRoot + uri;
536:
537: if (LOGGER.isDebugEnabled()) {
538: LOGGER.debug("requested file: " + filePath);
539: }
540:
541: if (!(new File(filePath).exists())) {
542: throw new XFormsException("File does not exist: " + filePath);
543: }
544:
545: return filePath;
546: }
547: */
548:
549: }
550:
551: //end of class
|