/* * XFormsFilter * Copyright (C) 2006 Adam Retter, Devon Portal Project <adam.retter@devon.gov.uk> * www.devonline.gov.uk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package uk.gov.devonline.www.xforms; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.Integer; import java.net.URI; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.xml.transform.TransformerFactory; import org.apache.commons.httpclient.Cookie; import org.apache.log4j.Logger; import org.chiba.adapter.ChibaAdapter; import org.chiba.adapter.ChibaEvent; import org.chiba.adapter.DefaultChibaEventImpl; import org.chiba.adapter.ui.UIGenerator; import org.chiba.adapter.ui.XSLTGenerator; import org.chiba.web.WebAdapter; import org.chiba.web.servlet.ChibaServlet; import org.chiba.web.servlet.HttpRequestHandler; import org.chiba.web.servlet.ServletAdapter; import org.chiba.web.session.XFormsSession; import org.chiba.web.session.XFormsSessionManager; import org.chiba.web.session.impl.DefaultXFormsSessionManagerImpl; import org.chiba.xml.events.ChibaEventNames; import org.chiba.xml.events.XMLEvent; import org.chiba.xml.xforms.config.Config; import org.chiba.xml.xforms.config.XFormsConfigException; import org.chiba.xml.xforms.connector.http.AbstractHTTPConnector; import org.chiba.xml.xforms.exception.XFormsException; import org.chiba.xml.xslt.TransformerService; import org.chiba.xml.xslt.impl.CachingTransformerService; import org.chiba.xml.xslt.impl.FileResourceResolver; /** * A Servlet Filter to provide XForms functionality to existing Servlets * * Currently borrows heavily from org.chiba.web.servlet.* in particular ChibaServlet * * * @author Adam Retter <adam.retter@devon.gov.uk> * @version 1.3 * @serial 2007-02-19T13:51 * * * Initialization parameters for the ServletFilter * * chiba.debug true or false indicating if Chiba should run in debug mode * chiba.config Path to Chiba's configuration file * chiba.upload Path to upload temporary files to * chiba.xslt.cache true or false indicating if Chiba should Cache XSLT * chiba.web.xslt.path Path to XSLT location * chiba.web.xslt.default XSLT file in XSLT_STYLESHEET_PATH to use for processing * chiba.CSSPath Path to xforms.css to use, relevant to servlet context * chiba.XFormsSessionChecking Period in milliseconds that XForms sessions should be checked * chiba.XFormsSessionTimeout Period in milliseconds of XForms session expiry when inactive */ public class XFormsFilter implements Filter { protected final static Logger LOG = Logger.getLogger(XFormsFilter.class); private FilterConfig filterConfig = null; private final static boolean DEFAULT_CHIBA_DEBUG = true; private final static String DEFAULT_CHIBA_CONFIG = "/eXist/tools/XFormsFilter/chiba.default.xml"; private final static String DEFAULT_CHIBA_TEMP_UPLOAD_DESTINATION = "/tmp"; private final static boolean DEFAULT_CHIBA_XSLT_CACHE = true; private final static String DEFAULT_CHIBA_STYLESHEET_PATH = "/exist/tools/XFormsFilter/xslt"; private final static String DEFAULT_CHIBA_STYLESHEET_FILE = "xhtml.xsl"; private final static String DEFAULT_CHIBA_CSS_PATH = "/servlet/db/system/xformsfilter"; private final static int DEFAULT_CHIBA_XFORMS_SESSION_CHECKING = 300000; private final static int DEFAULT_CHIBA_XFORMS_SESSION_TIMEOUT = 1200000; private boolean CHIBA_DEBUG = DEFAULT_CHIBA_DEBUG; private String CHIBA_CONFIG = DEFAULT_CHIBA_CONFIG; private String CHIBA_TEMP_UPLOAD_DESTINATION = DEFAULT_CHIBA_TEMP_UPLOAD_DESTINATION; private boolean CHIBA_XSLT_CACHE = DEFAULT_CHIBA_XSLT_CACHE; private String CHIBA_STYLESHEET_PATH = DEFAULT_CHIBA_STYLESHEET_PATH; private String CHIBA_STYLESHEET_FILE = DEFAULT_CHIBA_STYLESHEET_FILE; private String CHIBA_CSS_PATH = DEFAULT_CHIBA_CSS_PATH; private int CHIBA_XFORMS_SESSION_CHECKING = DEFAULT_CHIBA_XFORMS_SESSION_CHECKING; private int CHIBA_XFORMS_SESSION_TIMEOUT = DEFAULT_CHIBA_XFORMS_SESSION_TIMEOUT; private static final String XFORMS_NS = "http://www.w3.org/2002/xforms"; private static final String HTML_CONTENT_TYPE = "text/html;charset=UTF-8"; private final static String[] CHIBA_QUERYSTRING_PARAMS = { "form", "xslt", "action_url", "JavaScript" }; /** * Filter initialisation * * @see http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/Filter.html#init(javax.servlet.FilterConfig) */ public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; setupConfig(); setupTransformerService(); createXFormsSessionManager(); } /** * Filter shutdown * * @see http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/Filter.html#destroy() */ public void destroy() { // TODO Auto-generated method stub } /** * Set's up variables from the filter configuration */ private void setupConfig() { if(filterConfig.getInitParameter("chiba.debug") != null) { CHIBA_DEBUG = filterConfig.getInitParameter("chiba.debug").toLowerCase().equals("true"); } if(filterConfig.getInitParameter("chiba.config") != null) { CHIBA_CONFIG = filterConfig.getInitParameter("chiba.config"); } if(filterConfig.getInitParameter("chiba.upload") != null) { CHIBA_TEMP_UPLOAD_DESTINATION = filterConfig.getInitParameter("chiba.upload"); } if(filterConfig.getInitParameter("chiba.xslt.cache") != null) { CHIBA_XSLT_CACHE = filterConfig.getInitParameter("chiba.xslt.cache").toLowerCase().equals("true"); } if(filterConfig.getInitParameter("chiba.web.xslt.path") != null) { CHIBA_STYLESHEET_PATH = filterConfig.getInitParameter("chiba.web.xslt.path"); } if(filterConfig.getInitParameter("chiba.web.xslt.default") != null) { CHIBA_STYLESHEET_FILE = filterConfig.getInitParameter("chiba.web.xslt.default"); } if(filterConfig.getInitParameter("chiba.CSSPath") != null) { CHIBA_CSS_PATH = filterConfig.getInitParameter("chiba.CSSPath"); } if(filterConfig.getInitParameter("chiba.XFormsSessionChecking") != null) { CHIBA_XFORMS_SESSION_CHECKING = Integer.parseInt(filterConfig.getInitParameter("chiba.XFormsSessionChecking")); } if(filterConfig.getInitParameter("chiba.XFormsSessionTimeout") != null) { CHIBA_XFORMS_SESSION_TIMEOUT = Integer.parseInt(filterConfig.getInitParameter("chiba.XFormsSessionTimeout")); } } private void setupTransformerService() throws ServletException { TransformerService transformerService = new CachingTransformerService(new FileResourceResolver()); transformerService.setTransformerFactory(TransformerFactory.newInstance()); if(CHIBA_XSLT_CACHE) { LOG.debug("initializing xslt cache"); // load default stylesheet // todo: extract parameter names try { URI uri = new File(CHIBA_STYLESHEET_PATH).toURI().resolve(new URI(CHIBA_STYLESHEET_FILE)); transformerService.getTransformer(uri); } catch(Exception e) { throw new ServletException(e); } } // store service in servlet context // todo: contemplate about transformer service thread-safety filterConfig.getServletContext().setAttribute(TransformerService.class.getName(), transformerService); } private void createXFormsSessionManager() { XFormsSessionManager manager = DefaultXFormsSessionManagerImpl.getInstance(); manager.setInterval(CHIBA_XFORMS_SESSION_CHECKING); manager.setTimeout(CHIBA_XFORMS_SESSION_TIMEOUT); //start running the session cleanup manager.init(); } /** * The actual filtering method * * @see http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/Filter.html#doFilter(javax.servlet.ServletRequest,%20javax.servlet.ServletResponse,%20javax.servlet.FilterChain) */ public void doFilter(ServletRequest srvRequest, ServletResponse srvResponse, FilterChain filterChain) throws IOException, ServletException { /* before servlet request */ if(isXFormUpdateRequest(srvRequest)) { LOG.info("Start Update XForm"); updateXForm(srvRequest, srvResponse); LOG.info("End Update XForm"); } else { /* do servlet request */ LOG.info("Passing to Chain"); BufferedHttpServletResponseWrapper bufResponse = new BufferedHttpServletResponseWrapper((HttpServletResponse)srvResponse); filterChain.doFilter(srvRequest, bufResponse); LOG.info("Returned from Chain"); /* after servlet request */ if(hasXForm(bufResponse)) { bufResponse.getOutputStream().close(); LOG.info("Start Render XForm"); //remove DOCTYPE PI if it exists, Xerces in Chiba otherwise may try to download the system DTD (can cause latency problems) byte[] data = removeDocumentTypePI(bufResponse.getData()); //correct the <xforms:instance> xmlns="" problem (workaround for namespace problems in eXist) data = correctInstanceXMLNS(data); renderXForm(new ByteArrayInputStream(data), srvRequest, srvResponse); LOG.info("End Render XForm"); } else { srvResponse.getOutputStream().write(bufResponse.getData()); srvResponse.getOutputStream().close(); } } } /** * Removes the DOCTYPE Processing Instruction from the content if it exists * * @param content The HTML page content * * @return The content without the DOCTYPE PI */ public byte[] removeDocumentTypePI(byte[] content) { String buf = new String(content); int iStartDoctype = buf.indexOf("<!DOCTYPE"); if(iStartDoctype > -1) { int iEndDoctype = buf.indexOf('>', iStartDoctype); String newBuf = buf.substring(0, iStartDoctype - 1); newBuf += buf.substring(iEndDoctype + 1); return newBuf.getBytes(); } return content; } /** * Inserts the attribute xmlns="" on the xforms:instance node if it is missing * * @param content The HTML page content * * @return The content with the corrected xforms:instance */ public byte[] correctInstanceXMLNS(byte[] content) { String buf = new String(content); if(buf.indexOf("<xforms:instance xmlns=\"\">") == -1) { String newBuf = buf.replace("<xforms:instance>", "<xforms:instance xmlns=\"\">"); return newBuf.getBytes(); } return content; } /** * Checks if the request is to update an XForm * * @param srvRequest The request * * @return true if the request is to update an XForm, false otherwise */ public boolean isXFormUpdateRequest(ServletRequest srvRequest) { //get the http request object HttpServletRequest request = (HttpServletRequest)srvRequest; //must be a POST request if(!request.getMethod().equals("POST")) return false; //get the Chiba Adapter from the session String key = request.getParameter("sessionKey"); if(key == null) return false; XFormsSessionManager manager = (XFormsSessionManager)request.getSession().getAttribute(XFormsSessionManager.XFORMS_SESSION_MANAGER); XFormsSession xFormsSession = manager.getXFormsSession(key); if(xFormsSession == null) return false; //ChibaAdapter adapter = (ChibaAdapter)request.getSession().getAttribute(SESSION_CHIBA_ADAPTER); WebAdapter adapter = xFormsSession.getAdapter(); if(adapter == null) return false; String actionURL; if(request.getQueryString() != null) { actionURL = request.getRequestURL() + "?" + request.getQueryString(); } else { actionURL = request.getRequestURL().toString(); } //remove the sessionKey (if any) before comparing the action URL int posSessionKey = actionURL.indexOf("sessionKey"); if(posSessionKey > -1) { char preSep = actionURL.charAt(posSessionKey - 1); if(preSep == '?') { if(actionURL.indexOf('&') > -1) { actionURL = actionURL.substring(0, posSessionKey) + actionURL.substring(actionURL.indexOf('&') + 1); } else { actionURL = actionURL.substring(0, posSessionKey - 1); } } else if(preSep == '&') { actionURL = actionURL.substring(0, posSessionKey - 1); } } //if the action-url in the adapters context param is the same as that of the action url then we know we are updating return(adapter.getContextParam("action-url").equals(actionURL)); } /** * Checks if the response contains an XForm * * @param bufResponse The buffered response * * @return true if the response contains an XForm, false otherwise */ public boolean hasXForm(BufferedHttpServletResponseWrapper bufResponse) { String strResponse = bufResponse.getDataAsString(); //find the xforms namespace local name int xfNSDeclEnd = strResponse.indexOf("=\"" + XFORMS_NS + "\""); if(xfNSDeclEnd != -1) { String temp = strResponse.substring(0, xfNSDeclEnd); int xfNSDeclStart = temp.lastIndexOf(':') + 1; String xfNSLocal = temp.substring(xfNSDeclStart); //check for xforms model and instance elements if(strResponse.contains('<' + xfNSLocal + ":model") && strResponse.contains('<' + xfNSLocal + ":instance")) { return true; } } return false; } /** * Renders an XForm described in bufServletResponseWrapper using the Chiba XForms Engine * * @param srvRequest The Servlet request object * @param bufServletResponseWrapper The response from the Servlet */ public void renderXForm(InputStream isXForm, ServletRequest srvRequest, ServletResponse srvResponse) { //if no encoding specified default to UTF-8 if(srvRequest.getCharacterEncoding() == null) srvRequest.setCharacterEncoding("UTF-8"); HttpServletRequest request = (HttpServletRequest)srvRequest; HttpServletResponse response = (HttpServletResponse)srvResponse; HttpSession session = request.getSession(true); WebAdapter adapter = null; /* the XFormsSessionManager is kept in the http-session though it is accessible as singleton. Subsequent servlets should access the manager through the http-session attribute as below to ensure the http-session is refreshed. */ XFormsSessionManager sessionManager = getXFormsSessionManager(); XFormsSession xFormsSession = sessionManager.createXFormsSession(); session.setAttribute(XFormsSessionManager.XFORMS_SESSION_MANAGER, sessionManager); LOG.debug("Created XFormsSession with key: " + xFormsSession.getKey()); //Dont set in filter -> is set by servlet! /* request.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control","private, no-store, no-cache, must-revalidate"); response.setHeader("Pragma","no-cache"); response.setDateHeader("Expires",-1); */ try { //get the base URI String baseURI = request.getRequestURL().toString(); //get the action URL String actionURL; if(request.getQueryString() != null) { actionURL = request.getRequestURL() + "?" + request.getQueryString(); } else { actionURL = request.getRequestURL().toString(); } //new chiba adapter (ServletAdapter for HTML/XForms | FluxAdapter for AJAX/XForms) adapter = new ServletAdapter(); adapter = setupAdapter(adapter, xFormsSession, baseURI, isXForm); setContextParams(request, adapter); storeCookies(request, adapter); storeAcceptLanguage(request, adapter); //set some parameters for checking on POST adapter.setContextParam("action-url", actionURL); //initialise the adapter adapter.init(); //handle exit event XMLEvent exitEvent = adapter.checkForExitEvent(); if(exitEvent != null) { handleExit(exitEvent, xFormsSession, session, request, response); } else { //TODO: needed? //response.setContentType(HTML_CONTENT_TYPE); UIGenerator uiGenerator = createUIGenerator(request, xFormsSession, actionURL, null); //Generate the UI uiGenerator.setInput(adapter.getXForms()); uiGenerator.setOutput(response.getOutputStream()); uiGenerator.generate(); //store WebAdapter in XFormsSession xFormsSession.setAdapter(adapter); //store UIGenerator in XFormsSession as property xFormsSession.setProperty(XFormsSession.UIGENERATOR, uiGenerator); //store queryString as 'referer' in XFormsSession xFormsSession.setProperty(XFormsSession.REFERER, request.getQueryString()); //actually add the XFormsSession ot the manager sessionManager.addXFormsSession(xFormsSession); } } catch(Exception e) { LOG.error(e); e.printStackTrace(); shutdown(adapter, session, e, response, request, xFormsSession.getKey()); } } /** * Updates the state of an XForm * * @param srvRequest The Servlet request object to read the update from * @param srvResponse The Servlet response to write the result to */ private void updateXForm(ServletRequest srvRequest, ServletResponse srvResponse) { HttpServletRequest request = (HttpServletRequest)srvRequest; HttpServletResponse response = (HttpServletResponse)srvResponse; HttpSession session = request.getSession(); WebAdapter adapter = null; String key = request.getParameter("sessionKey"); /* * (ChibaAdapter)session.getAttribute(SESSION_CHIBA_ADAPTER); */ try { XFormsSessionManager manager = (XFormsSessionManager)session.getAttribute(XFormsSessionManager.XFORMS_SESSION_MANAGER); XFormsSession xFormsSession = manager.getXFormsSession(key); String referer = (String)xFormsSession.getProperty(XFormsSession.REFERER); LOG.debug("Referer: " + referer); adapter = xFormsSession.getAdapter(); if(adapter == null) { LOG.error("No chiba adapter in session!"); return; } ChibaEvent chibaEvent = new DefaultChibaEventImpl(); chibaEvent.initEvent("http-request", null, request); adapter.dispatch(chibaEvent); //handle exit event XMLEvent exitEvent = adapter.checkForExitEvent(); if(exitEvent != null) { handleExit(exitEvent, xFormsSession, session, request, response); } else { //TODO: needed? //response.setContentType(HTML_CONTENT_TYPE); //Dont set in filter -> is set by servlet! /* request.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control","private, no-store, no-cache, must-revalidate"); response.setHeader("Pragma","no-cache"); response.setDateHeader("Expires",-1); */ UIGenerator uiGenerator = (UIGenerator)xFormsSession.getProperty(XFormsSession.UIGENERATOR); //TODO: needed? response.setCharacterEncoding("UTF-8"); uiGenerator.setInput(adapter.getXForms()); uiGenerator.setOutput(response.getOutputStream()); uiGenerator.generate(); response.getOutputStream().close(); } } catch(Exception e) { LOG.error(e); e.printStackTrace(); shutdown(adapter, session, e, response, request, key); } } /** * configures the an Adapter for interacting with the XForms processor (ChibaBean). The Adapter itself * will create the XFormsProcessor (ChibaBean) and configure it for processing. * <p/> * If you'd like to use a different source of XForms documents e.g. DOM you should extend this class and * overwrite this method. Please take care to also set the baseURI of the processor to a reasonable value * cause this will be the fundament for all URI resolutions taking place. * * @param adapter the WebAdapter implementation to setup * @param XFormsSession The XFormsSession for this adapter * @param baseURI The URI to use as a base for resolving URI's * @param isXForm The input stream for the XForm * * @return ServletAdapter */ protected WebAdapter setupAdapter(WebAdapter adapter, XFormsSession xFormsSession, String baseURI, InputStream isXForm) throws XFormsException, URISyntaxException, IOException { //set the adapters xforms session adapter.setXFormsSession(xFormsSession); //set the config file adapter.setConfigPath(CHIBA_CONFIG); //set the xform, then close the input stream adapter.setXForms(isXForm); isXForm.close(); //set the base URI adapter.setBaseURI(baseURI); //set the temporary file upload location adapter.setUploadDestination(CHIBA_TEMP_UPLOAD_DESTINATION); Map servletMap = new HashMap(); servletMap.put(WebAdapter.SESSION_ID, xFormsSession.getKey()); adapter.setContextParam(ChibaAdapter.SUBMISSION_RESPONSE, servletMap); return adapter; } /** * this method is responsible for passing all context information needed by the Adapter and Processor from * ServletRequest to ChibaContext. Will be called only once when the form-session is inited (GET). * * @param request the ServletRequest * @param adapter the WebAdapter to use */ protected void setContextParams(HttpServletRequest request, WebAdapter adapter) { //[1] pass user-agent to Adapter for UI-building adapter.setContextParam(WebAdapter.USERAGENT, request.getHeader("User-Agent")); adapter.setContextParam(WebAdapter.REQUEST_URI, getRequestURI(request)); //[2] read any request params that are *not* Chiba params and pass them into the context map Enumeration params = request.getParameterNames(); String paramName; while(params.hasMoreElements()) { paramName = (String) params.nextElement(); boolean isChibaParam = false; for(int i = 0; i < CHIBA_QUERYSTRING_PARAMS.length; i++) { if(paramName.equals(CHIBA_QUERYSTRING_PARAMS[i])) { isChibaParam = true; } } if(!isChibaParam) { String paramValue = request.getParameter(paramName); adapter.setContextParam(paramName, paramValue); LOG.debug("Request param: " + paramName + " added to the context"); } } } /** * stores cookies that may exist in request and passes them on to processor for usage in * HTTPConnectors. Instance loading and submission then uses these cookies. Important for * applications using auth. * * @param request the servlet request * @param adapter the WebAdapter instance */ protected void storeCookies(HttpServletRequest request, WebAdapter adapter) { javax.servlet.http.Cookie[] cookiesIn = request.getCookies(); if(cookiesIn != null) { Cookie[] commonsCookies = new Cookie[cookiesIn.length]; for(int i = 0; i < cookiesIn.length; i += 1) { javax.servlet.http.Cookie c = cookiesIn[i]; String domain = c.getDomain(); if(domain == null) { domain = ""; } String path = c.getPath(); if(path == null) { path = "/"; } commonsCookies[i] = new Cookie(domain, c.getName(), c.getValue(), path, c.getMaxAge(), c.getSecure()); } adapter.setContextParam(AbstractHTTPConnector.REQUEST_COOKIE, commonsCookies); } } protected void storeAcceptLanguage(HttpServletRequest request, WebAdapter adapter) { String acceptLanguage = request.getHeader("accept-language"); if((acceptLanguage != null) && (acceptLanguage.length() > 0)) { adapter.setContextParam(AbstractHTTPConnector.ACCEPT_LANGUAGE, acceptLanguage); } } protected UIGenerator createUIGenerator(HttpServletRequest request, XFormsSession xFormsSession, String actionURL, String js) throws URISyntaxException, XFormsConfigException { TransformerService transformerService = (TransformerService) filterConfig.getServletContext().getAttribute(TransformerService.class.getName()); URI uri = new File(CHIBA_STYLESHEET_PATH).toURI().resolve(new URI(CHIBA_STYLESHEET_FILE)); XSLTGenerator generator = new XSLTGenerator(); generator.setTransformerService(transformerService); generator.setStylesheetURI(uri); // todo: unify and extract parameter names generator.setParameter("contextroot", request.getContextPath()); generator.setParameter("sessionKey", xFormsSession.getKey()); if(xFormsSession.getProperty(XFormsSession.KEEPALIVE_PULSE) != null) { generator.setParameter("keepalive-pulse", xFormsSession.getProperty(XFormsSession.KEEPALIVE_PULSE)); } generator.setParameter("action-url", actionURL); generator.setParameter("debug-enabled", CHIBA_DEBUG); String selectorPrefix = Config.getInstance().getProperty(HttpRequestHandler.SELECTOR_PREFIX_PROPERTY, HttpRequestHandler.SELECTOR_PREFIX_DEFAULT); generator.setParameter("selector-prefix", selectorPrefix); String removeUploadPrefix = Config.getInstance().getProperty(HttpRequestHandler.REMOVE_UPLOAD_PREFIX_PROPERTY, HttpRequestHandler.REMOVE_UPLOAD_PREFIX_DEFAULT); generator.setParameter("remove-upload-prefix", removeUploadPrefix); String dataPrefix = Config.getInstance().getProperty("chiba.web.dataPrefix"); generator.setParameter("data-prefix", dataPrefix); String triggerPrefix = Config.getInstance().getProperty("chiba.web.triggerPrefix"); generator.setParameter("trigger-prefix", triggerPrefix); generator.setParameter("user-agent", request.getHeader("User-Agent")); generator.setParameter("scripted", String.valueOf(js != null)); //TODO: enable for AJAX/XForms /*if(scriptPath != null) { generator.setParameter("scriptPath", scriptPath); }*/ generator.setParameter("CSSPath", CHIBA_CSS_PATH + '/'); return generator; } private String getRequestURI(HttpServletRequest request) { StringBuffer buffer = new StringBuffer(request.getScheme()); buffer.append("://"); buffer.append(request.getServerName()); buffer.append(":"); buffer.append(request.getServerPort()); buffer.append(request.getContextPath()); return buffer.toString(); } /** * returns a specific implementation of XFormsSessionManager. Plugin your own implementations here if needed. * * @return a specific implementation of XFormsSessionManager (defaults to DefaultXFormsSessionManagerImpl) */ protected XFormsSessionManager getXFormsSessionManager() { return DefaultXFormsSessionManagerImpl.getInstance(); } protected void handleExit(XMLEvent exitEvent, XFormsSession xFormsSession, HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException { if(ChibaEventNames.REPLACE_ALL.equals(exitEvent.getType())) { submissionResponse(xFormsSession, request, response); //response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/SubmissionResponse?sessionKey=" + xFormsSession.getKey())); } else if(ChibaEventNames.LOAD_URI.equals(exitEvent.getType())) { if(exitEvent.getContextInfo("show") != null) { String loadURI = (String) exitEvent.getContextInfo("uri"); //kill XFormsSession xFormsSession.getManager().deleteXFormsSession(xFormsSession.getKey()); response.sendRedirect(response.encodeRedirectURL(loadURI)); } } LOG.debug("EXITED DURING XFORMS MODEL INIT!"); } private void submissionResponse(XFormsSession xFormsSession, HttpServletRequest request, HttpServletResponse response) throws IOException { // lookup attribute containing submission response map Map submissionResponse = (Map)xFormsSession.getProperty(ChibaServlet.CHIBA_SUBMISSION_RESPONSE); if(submissionResponse != null) { LOG.debug("Handling submission/@replace='all'"); // copy header fields Map headerMap = (Map)submissionResponse.get("header"); String name; String value; Iterator iterator = headerMap.keySet().iterator(); while(iterator.hasNext()) { name = (String) iterator.next(); if(name.equalsIgnoreCase("Transfer-Encoding")) { // Some servers (e.g. WebSphere) may set a "Transfer-Encoding" // with the value "chunked". This may confuse the client since // ChibaServlet output is not encoded as "chunked", so this // header is ignored. continue; } value = (String) headerMap.get(name); response.setHeader(name, value); LOG.debug("Added header: " + name + "=" + value); } // copy body stream InputStream bodyStream = (InputStream) submissionResponse.get("body"); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); for(int b = bodyStream.read(); b > -1; b = bodyStream.read()) { outputStream.write(b); } // close streams bodyStream.close(); outputStream.close(); //kill XFormsSession XFormsSessionManager sessionManager = xFormsSession.getManager(); sessionManager.deleteXFormsSession(xFormsSession.getKey()); return; } response.sendError(HttpServletResponse.SC_FORBIDDEN, "no submission response available"); } protected void shutdown(WebAdapter adapter, HttpSession session, Exception e, HttpServletResponse response, HttpServletRequest request, String key) { // attempt to shutdown processor if(adapter != null) { try { adapter.shutdown(); } catch(XFormsException xfe) { LOG.error(xfe); xfe.printStackTrace(); } } // store exception //todo: move exceptions to XFormsSession session.setAttribute("chiba.exception", e); //remove xformssession from httpsession session.removeAttribute(key); //TODO: should we pass the error upto the servlet somehow? // redirect to error page (after encoding session id if required) //response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/" + request.getSession().getServletContext().getInitParameter("error.page"))); } }