Java tutorial
/* Copyright (C) 2007-2011 BlueXML - www.bluexml.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.bluexml.xforms.controller.navigation; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.alfresco.service.namespace.QName; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chiba.agent.web.WebFactory; import org.chiba.xml.xforms.core.Submission; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.bluexml.side.form.utils.DOMUtil; import com.bluexml.xforms.actions.AbstractAction; import com.bluexml.xforms.actions.CancelAction; import com.bluexml.xforms.actions.CreateClassAction; import com.bluexml.xforms.actions.CreateFormAction; import com.bluexml.xforms.actions.DeleteAction; import com.bluexml.xforms.actions.EditClassAction; import com.bluexml.xforms.actions.EditFormAction; import com.bluexml.xforms.actions.EnumAction; import com.bluexml.xforms.actions.ExecuteAction; import com.bluexml.xforms.actions.GetAction; import com.bluexml.xforms.actions.ListAction; import com.bluexml.xforms.actions.SetTypeAction; import com.bluexml.xforms.actions.SubmitAction; import com.bluexml.xforms.actions.WorkflowSaveAction; import com.bluexml.xforms.actions.WorkflowTransitionAction; import com.bluexml.xforms.controller.alfresco.AlfrescoController; import com.bluexml.xforms.controller.alfresco.AlfrescoTransaction; import com.bluexml.xforms.controller.beans.PageInfoBean; import com.bluexml.xforms.messages.MsgId; import com.bluexml.xforms.messages.MsgPool; /** * The Class NavigationManager. */ public class NavigationManager { /** The Constant ACTIONS. */ private static final Class<?>[] ACTIONS = { CancelAction.class, CreateClassAction.class, CreateFormAction.class, EditClassAction.class, EditFormAction.class, EnumAction.class, ExecuteAction.class, GetAction.class, ListAction.class, SubmitAction.class, DeleteAction.class, SetTypeAction.class, /* * WorkflowFormGetAction.class, WorkflowGetAction.class, * WorkflowInstanceListAction.class, * WorkflowProcessListAction.class, * WorkflowStartAction.class, */ WorkflowTransitionAction.class, WorkflowSaveAction.class }; /** The Constant PAGE_ID. */ public static final String PAGE_ID = "pageId"; /** The Constant TEMPLATE_ID. */ public static final String TEMPLATE_ID = "template"; /** The string by which we accept an upload path. */ public static final String UPLOADPATH_ID = "uploadPath"; /** The Constant instance. */ protected static final NavigationManager instance = new NavigationManager(); /** The actions. */ protected Map<String, Class<? extends AbstractAction>> actions; /** The logger. */ protected static Log logger = LogFactory.getLog(NavigationManager.class); // /** The servlet url. */ // protected String servletURL = null; // /** The servletURLs identified by sessionId. */ // protected Map<String, String> servletURLs; // /** The document transformer. */ protected Transformer documentTransformer; /** The document builder. */ protected DocumentBuilder documentBuilder; private AlfrescoController controller; /** * Gets the single instance of NavigationManager. * * @return single instance of NavigationManager */ public static NavigationManager getInstance() { return instance; } /** * Instantiates a new navigation manager. */ private NavigationManager() { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilder = documentBuilderFactory.newDocumentBuilder(); documentTransformer = TransformerFactory.newInstance().newTransformer(); documentTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); } catch (Exception e) { throw new RuntimeException(e); } controller = getController(); List<Class<?>> classes = new ArrayList<Class<?>>(ACTIONS.length); classes.addAll(Arrays.asList(ACTIONS)); // InputStream actionsXmlStream = // NavigationManager.class.getResourceAsStream("/actions.xml"); // if (actionsXmlStream != null) { // try { // Document actionsDocument = documentBuilder.parse(actionsXmlStream); // addActions(actionsDocument, classes); // } catch (Exception e) { // logger.error(e); // } // } actions = new TreeMap<String, Class<? extends AbstractAction>>(); for (Class<?> classAction : classes) { try { AbstractAction abstractAction = (AbstractAction) classAction.newInstance(); actions.put(abstractAction.getActionName(), abstractAction.getClass()); } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error("Error while registering actions", e); } } } } /** * Adds the actions. * * @param actionsDocument * the actions document * @param classes * the classes * @deprecated */ @Deprecated @SuppressWarnings("unused") private void addActions(Document actionsDocument, List<Class<?>> classes) { NodeList actionNodes = actionsDocument.getDocumentElement().getChildNodes(); for (int i = 0; i < actionNodes.getLength(); i++) { Node item = actionNodes.item(i); if (item instanceof Element) { Element action = (Element) item; if (action.getTagName().equals("action")) { String className = null; NodeList childNodes = action.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { Node childNode = childNodes.item(j); if (childNode instanceof Element) { Element subElement = (Element) childNode; if (subElement.getTagName().equals("class")) { className = StringUtils.trimToNull(subElement.getTextContent()); } } } if (className != null) { try { Class<?> actionClassName = Class.forName(className); classes.add(actionClassName); } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error(e); } } } } } } } /** * Log. * * @param messages * the messages */ private void log(String... messages) { if (logger.isDebugEnabled()) { for (String message : messages) { logger.debug(message); } } } /** * Log xml. * * @param node * the node * @param messages * the messages */ private void logXML(Node node, String... messages) { if (logger.isDebugEnabled()) { log(messages); if (node != null) { Source xmlSource = new DOMSource(node); StringWriter sw = new StringWriter(); Result outputTarget = new StreamResult(sw); try { documentTransformer.transform(xmlSource, outputTarget); } catch (TransformerException e) { logger.error(e); } logger.debug(sw.toString()); } } } /** * Send XForms to Chiba filter.<br> * Inserts session id into form.<br> * No data manipulation has to be made here. * * @param req * the req * @param resp * the resp * @throws ServletException * the servlet exception * @throws IOException * Signals that an I/O exception has occurred. */ public void sendXForms(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(true); String sessionId = session.getId(); controller = getController(); String testStr = StringUtils.trimToNull(req.getParameter(MsgId.PARAM_SERVE_TEST_PAGE.getText())); boolean serveTestPage = StringUtils.equals(testStr, "true"); String pageId = StringUtils.trimToNull(req.getParameter(PAGE_ID)); // called from a direct link? set our info (pageId, stackId) if (pageId == null) { // check for a possible initialisation call boolean isInit = StringUtils.equals(req.getParameter(MsgId.PARAM_INIT_CALL.getText()), "true"); if (isInit) { ServletOutputStream stream = resp.getOutputStream(); String result = (loadConfiguration(req, true) == -1) ? "success" : "failure"; stream.write(result.getBytes()); stream.close(); return; } pageId = NavigationSessionListener.getPageId(sessionId); NavigationPath navigationPath = NavigationSessionListener.getNavigationPath(sessionId, pageId); // check whether reloading of the mapping.xml file was asked for if (StringUtils.equals(req.getParameter(MsgId.PARAM_RELOAD_MAPPING_FILE.getText()), "true")) { controller.performDynamicReload(); } // check whether reloading of properties/configuration files was asked for if (StringUtils.equals(req.getParameter(MsgId.PARAM_RELOAD_PROPERTIES.getText()), "true")) { int resLoad = loadConfiguration(req, false); if (logger.isDebugEnabled()) { if (resLoad == -1) { logger.debug("Reloaded properties: OK."); } else { String reason = ""; switch (resLoad) { case 0: reason = "an exception occured"; break; case 1: reason = "properties files"; break; case 2: reason = "redirection file"; break; } logger.debug("Failed in loading the configuration. Reason: " + reason); } } } // set specific CSS if given this.setCssUrl(req); // initial status message. CAUTION: may be overridden later in case of errors. String statusMsg = StringUtils.trimToNull(req.getParameter(MsgId.PARAM_STATUS_MSG.getText())); if (statusMsg != null) { navigationPath.setStatusMsg(statusMsg); } // deal with standalone mode if (StringUtils.equals(req.getParameter(MsgId.PARAM_STANDALONE.getText()), "true")) { controller.setStandaloneMode(true); } if (StringUtils.equals(req.getParameter(MsgId.PARAM_STANDALONE.getText()), "false")) { controller.setStandaloneMode(false); } PageInfoBean pageInfo = collectPageInfo(req); // save session and URL we were called with (useful when a host is multi-domain'ed) String curServletURL = this.registerSessionURL(req, sessionId); // remember where we are navigationPath.setCurrentPage(pageInfo); String location = curServletURL + "?pageId=" + pageId + "&stackId=" + navigationPath.getSize(); // propagate queryString location += "&" + req.getQueryString(); if (serveTestPage == false) { // redirect the web client, providing ids we need resp.sendRedirect(resp.encodeRedirectURL(location)); return; } } // the ids are available NavigationPath navigationPath = NavigationSessionListener.getNavigationPath(sessionId, pageId); if (navigationPath.isEmpty()) { // the servlet is called directly with ids we did not register throw new ServletException(MsgPool.getMsg(MsgId.MSG_SESSION_TIMED_OUT)); } Page currentPage = navigationPath.peekCurrentPage(); // set the warning if page was called with an object it can't display if (currentPage.isWrongCallType()) { navigationPath.setStatusMsg("WARNING: the data Id provided is not appropriate for this form."); } // get the form template as a string String statusDisplayedMsg = navigationPath.getStatusDisplayedMsg(); Document doc = loadXFormsDocument(req, sessionId, pageId, statusDisplayedMsg, currentPage); req.setAttribute(WebFactory.XFORMS_NODE, doc); resp.getOutputStream().close(); } /** * @param req * @param sessionId * @param pageId * @param statusDisplayedMsg * @param currentPage * @return * @throws IOException * @throws ServletException * @throws DOMException */ private Document loadXFormsDocument(HttpServletRequest req, String sessionId, String pageId, String statusDisplayedMsg, Page currentPage) throws IOException, ServletException, DOMException { String xformsString; String dataType = currentPage.getFormName(); FormTypeEnum formType = currentPage.getFormType(); boolean dataTypeSet = currentPage.isDataTypeSet(); String templateId = currentPage.getTemplate(); String pageLanguage = currentPage.getLanguage(); Map<String, String> initParams = currentPage.getInitParams(); xformsString = getXFormsString(formType, dataType, dataTypeSet, templateId, sessionId, pageId, req.getContextPath(), pageLanguage, initParams, req); Document doc; // try { // doc = org.chiba.xml.dom.DOMUtil.parseString(xformsString, true, false); // } catch (ParserConfigurationException e) { // throw new ServletException(e); // } catch (SAXException e) { // throw new ServletException(e); // } doc = DOMUtil.getDocumentFromString(xformsString); if (doc == null) { throw new RuntimeException("Unable to build the XForms document"); } Element docElt = doc.getDocumentElement(); // add CSS file if one is provided if (StringUtils.trimToNull(controller.getCssUrl()) != null) { Element head = DOMUtil.getChild(docElt, "xhtml:head"); Element css = doc.createElementNS("http://www.w3.org/1999/xhtml", "link"); css.setAttribute("rel", "stylesheet"); css.setAttribute("type", "text/css"); css.setAttribute("href", controller.getCssUrl()); head.appendChild(css); } // hide buttons if applicable if (currentPage.isShowSubmitButtons() == false) { String tagName = MsgId.INT_SUBMIT_BUTTONS_GROUP_ID.getText(); DOMUtil.removeEltInDescentByAttrValue(docElt, "id", tagName); } else { // #1181: individual hiding of submission buttons if (currentPage.isShowCancel() == false) { removeSubmitButton(docElt, MsgId.CAPTION_BUTTON_CANCEL); } if (currentPage.isShowDelete() == false) { removeSubmitButton(docElt, MsgId.CAPTION_BUTTON_DELETE); } if (currentPage.isShowValidate() == false) { removeSubmitButton(docElt, MsgId.CAPTION_BUTTON_SUBMIT); } } // add status message String statusMsg = statusDisplayedMsg; if (statusMsg != null) { String tagVal = MsgId.INT_CSS_STATUS_BAR_ID.getText(); Element status = DOMUtil.getEltInDescentByAttrValue(docElt, "id", tagVal); status.setTextContent(statusMsg); } return doc; } /** * Removes a specific submit button from the group of submit buttons. * * @param docElt * @param buttonId */ private void removeSubmitButton(Element docElt, MsgId buttonId) { String realCaption = MsgPool.getMsg(buttonId); Element button = null; // find the group of submits String groupId = MsgId.INT_SUBMIT_BUTTONS_GROUP_ID.getText(); Element submitGroup = DOMUtil.getEltInDescentByAttrValue(docElt, "id", groupId); // find the "xf:submit" node that has the right caption in its // "xf:label" List<Element> listOfSubmits = DOMUtil.getAllChildren(submitGroup); for (Element aSubmit : listOfSubmits) { if (StringUtils.equals(aSubmit.getTagName(), "xf:submit")) { Element label = DOMUtil.getChild(aSubmit, "xf:label"); if (StringUtils.equals(label.getTextContent(), realCaption)) { button = aSubmit; break; } } } if (button != null) { submitGroup.removeChild(button); } } /** * Registers the URL to associate with a session id. * * @param req * @param sessionId * @return */ private String registerSessionURL(HttpServletRequest req, String sessionId) { String curServletURL = NavigationSessionListener.getServletURL(sessionId); if (logger.isDebugEnabled()) { logger.debug("--> looking up session id '" + sessionId + "' for url: " + curServletURL); } if (curServletURL == null) { curServletURL = req.getRequestURL().toString(); if (NavigationSessionListener.registerServletURL(sessionId, curServletURL)) { if (logger.isInfoEnabled()) { logger.info("Registered session '" + sessionId + "' for url: " + curServletURL); } } else { if (logger.isInfoEnabled()) { logger.info("FAILED to register session '" + sessionId + "' for url: " + curServletURL); } } } return curServletURL; } /** * @param req * @return -1 if all files are loaded OK. 0 if an exception occurred. 1 if * failed at properties * files. 2 if failed at redirection file. * @throws IOException * @throws Exception */ private int loadConfiguration(HttpServletRequest req, boolean fromInitCall) { int result = -1; try { String formsPath = req.getParameter(MsgId.PARAM_PROPERTIES_FILE_FORMS.getText()); String msgPath = req.getParameter(MsgId.PARAM_PROPERTIES_FILE_MESSAGES.getText()); if (AlfrescoController.loadProperties(formsPath, msgPath) == false) { return 1; } String redirectPath = req.getParameter(MsgId.PARAM_REDIRECTOR_CONFIG_FILE.getText()); if (controller.loadRedirectionTable(redirectPath) == false) { result = 2; // continue anyway cause some projects may not use workflows } if (fromInitCall) { // we'll also deal with CSS, alfrescoHost this.setCssUrl(req); String host = req.getParameter(MsgId.PARAM_ALFRESCO_HOST.getText()); if (StringUtils.trimToNull(host) != null) { controller.setAlfrescoUrl(host); } } } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error("Couldn't load all configuration files.", e); } return 0; } return result; } /** * @param req */ private void setCssUrl(HttpServletRequest req) { String curCssUrl = StringUtils.trimToNull(req.getParameter(MsgId.PARAM_CSS_FILE.getText())); controller.setCssUrl(StringUtils.trimToNull(curCssUrl)); } /** * Assembles some page-specific info from the request into a bean: formType, * dataType, language, * dataId, etc. * * @param req * the HTTP request * @return the bean with the info */ private PageInfoBean collectPageInfo(HttpServletRequest req) throws ServletException { PageInfoBean bean = new PageInfoBean(); // init params bean.setInitParams(getInitParams(req)); // form type FormTypeEnum formType = FormTypeEnum.CLASS; // <-- default type String paramFormType = req.getParameter(MsgId.PARAM_FORM_TYPE.getText()); if (StringUtils.equals(paramFormType, MsgId.INT_FORMTYPE_FORM.getText())) { formType = FormTypeEnum.FORM; } if (StringUtils.equals(paramFormType, MsgId.INT_FORMTYPE_LIST.getText())) { formType = FormTypeEnum.LIST; } if (StringUtils.equals(paramFormType, MsgId.INT_FORMTYPE_SEARCH.getText())) { formType = FormTypeEnum.SEARCH; } if (StringUtils.equals(paramFormType, MsgId.INT_FORMTYPE_SELECTOR.getText())) { formType = FormTypeEnum.SELECTOR; } if (StringUtils.equals(paramFormType, MsgId.INT_FORMTYPE_WKFLW.getText())) { formType = FormTypeEnum.WKFLW; } bean.setFormType(formType); // type-related String originalDatatype = req.getParameter(MsgId.PARAM_DATA_TYPE.getText()); String dataType = null; if (formType == FormTypeEnum.CLASS) { dataType = StringUtils.trimToNull(originalDatatype).replace('_', '.'); } else if (formType == FormTypeEnum.SELECTOR) { dataType = originalDatatype.substring(0, originalDatatype.indexOf(MsgId.INT_SUFFIX_FILENAME_SELECTORS.getText())); } else { dataType = originalDatatype; } if (dataType == null) { throw new ServletException("type cannot be null"); } bean.setFormName(dataType); // #1222 AlfrescoController controller = getController(); bean.setDataType(controller.getDataTypeFromFormName(bean.getFormName())); bean.setWrongCallType(false); String dataId = StringUtils.trimToNull(req.getParameter(MsgId.PARAM_DATA_ID.getText())); // check that the form is appropriate for the data id if (controller.getParamCheckMatchDataForm(bean.getInitParams())) { String realFormName = originalDatatype; // if (bean.formType == FormTypeEnum.WKFLW) { // WorkflowTaskType taskType = controller.getWorkflowTaskType(dataType); // realFormName = taskType.getDataForm(); // } if (dataId != null) { String userName = controller.getParamUserName(bean.getInitParams()); AlfrescoTransaction transaction = new AlfrescoTransaction(controller, userName); QName contentType = controller.systemGetNodeType(transaction, dataId); if (contentType == null) { dataId = null; logger.error("Content is not valid - Form is opened without Content Id."); } else { if (bean.getFormType() == FormTypeEnum.WKFLW) { dataType = controller.getUnderlyingTypeForWorkflow(realFormName); if (dataType == null) { logger.error( "Workflow form is not associated to a Form data - Form is opened without passing Content Id. Use the parameter 'checkMatchDF=false' if you want to keep the content id"); } } else { dataType = controller.getUnderlyingTypeForForm(realFormName); } if (StringUtils.equals(dataType, contentType.getLocalName()) == false) { bean.setWrongCallType(true); dataId = null; } } } } bean.setDataId(controller.patchDataId(dataId)); bean.setTemplateId(req.getParameter(TEMPLATE_ID)); bean.setLanguage(req.getParameter(MsgId.PARAM_LANGUAGE.getText())); bean.setProcessId(req.getParameter(MsgId.PARAM_WORKFLOW_PROCESS_ID.getText())); bean.setInstanceId(req.getParameter(MsgId.PARAM_WORKFLOW_INSTANCE_ID.getText())); // ** #1421: support for mass tagging of multiple objects String ids = StringUtils.trimToNull(req.getParameter(MsgId.PARAM_MASS_IDS.getText())); if (ids != null) { bean.setMassTagging(true); bean.setMassIds(ids); } // ** #1421 // deal with submit buttons String submitParam; submitParam = req.getParameter(MsgId.PARAM_SHOW_SUBMITS.getText()); if ((StringUtils.trimToNull(submitParam) != null) && (StringUtils.equalsIgnoreCase(submitParam, "false"))) { bean.setShowSubmits(false); } // submitParam = req.getParameter(MsgId.PARAM_SHOW_CANCEL.getText()); if ((StringUtils.trimToNull(submitParam) != null) && (StringUtils.equalsIgnoreCase(submitParam, "false"))) { bean.setShowCancel(false); } // submitParam = req.getParameter(MsgId.PARAM_SHOW_DELETE.getText()); if ((StringUtils.trimToNull(submitParam) != null) && (StringUtils.equalsIgnoreCase(submitParam, "false"))) { bean.setShowDelete(false); } // submitParam = req.getParameter(MsgId.PARAM_SHOW_VALIDATE.getText()); if ((StringUtils.trimToNull(submitParam) != null) && (StringUtils.equalsIgnoreCase(submitParam, "false"))) { bean.setShowValidate(false); } return bean; } /** * Gets a map of all non-empty parameters provided in the request, skipping * some of those we are * interested in (e.g. "pageId"). * * @param req * the req * @return the inits the params */ @SuppressWarnings("all") private Map<String, String> getInitParams(HttpServletRequest req) { Map<String, String> result = new TreeMap<String, String>(); Map<String, String[]> parameters = req.getParameterMap(); Set<Entry<String, String[]>> entrySet = parameters.entrySet(); for (Entry<String, String[]> entry : entrySet) { String key = entry.getKey(); if (!StringUtils.equals(key, PAGE_ID) && !StringUtils.equals(key, MsgId.PARAM_DATA_TYPE.getText())) { String[] values = entry.getValue(); if (values.length > 0) { result.put(key, values[0]); } } } return result; } /** * Locates a generated XForms template file and returns its contents as a * string in which * placeholders are replaced. Placeholders for indirected user messages and * dynamic enums are * also replaced. * * @param formType * the form type, used to select the appropriate subfolder * @param dataType * the data type * @param dataTypeSet * the data type set, for abstract types, used to show selector * forms instead of * actual forms * @param template * the template... ;-? * @param sessionId * the session id, whose placeholder is * INT_GEN_PLACEHOLDER_SESSION_ID * @param pageId * the page id * @param contextPath * the context path, whose placeholder is * INT_GEN_PLACEHOLDER_CONTEXT_PATH * @param pageLanguage * @param initParams * the parameters in the uri * @param req * @return the x forms string * @throws IOException * Signals that an I/O exception has occurred. * @throws ServletException * the servlet exception */ private String getXFormsString(FormTypeEnum formType, String dataType, boolean dataTypeSet, String template, String sessionId, String pageId, String contextPath, String pageLanguage, Map<String, String> initParams, HttpServletRequest req) throws IOException, ServletException { StringBuffer xformsStringBuffer = new StringBuffer(); InputStream xformsStream = null; String baseFolder = controller.getParamBaseFolder(initParams) + File.separator + "forms" + File.separator; if (logger.isDebugEnabled()) { logger.debug("Trying to load form '" + dataType + " from base folder: '" + baseFolder + "'"); } String base = null; // the directory and filename (without extension) // no path given, we use the default behavior if (formType == FormTypeEnum.FORM) { base = MsgId.INT_DIRECTORY_FORM_FORMS.getText() + File.separatorChar + dataType; } else if (formType == FormTypeEnum.SEARCH) { base = MsgId.INT_DIRECTORY_FORM_SEARCH.getText() + File.separatorChar + dataType; } else if (formType == FormTypeEnum.WKFLW) { base = MsgId.INT_DIRECTORY_FORM_WKFLW.getText() + File.separatorChar + dataType; } else if (formType == FormTypeEnum.LIST) { base = MsgId.INT_DIRECTORY_FORM_LISTS.getText() + File.separatorChar + dataType; } else if (formType == FormTypeEnum.SELECTOR) { base = MsgId.INT_DIRECTORY_FORM_SELECTOR.getText() + File.separatorChar + dataType + MsgId.INT_SUFFIX_FILENAME_SELECTORS; } else { if (!dataTypeSet) { if (getController().hasSubTypes(dataType)) { base = MsgId.INT_DIRECTORY_FORM_SELECTOR.getText() + File.separatorChar + dataType + MsgId.INT_SUFFIX_FILENAME_SELECTORS; } else { base = MsgId.INT_DIRECTORY_FORM_CLASSES.getText() + File.separatorChar + dataType; } } else { base = MsgId.INT_DIRECTORY_FORM_CLASSES.getText() + File.separatorChar + dataType; } } // xformsStream = NavigationSessionListener.getContext().getResourceAsStream( // "/forms/custom/" + base + "_" + template + ".xhtml"); xformsStream = getInputStream(baseFolder + "custom" + File.separator + base + (template != null ? "_" : "") + StringUtils.trimToEmpty(template) + ".xhtml"); if (xformsStream == null) { xformsStream = getInputStream(baseFolder + "custom" + File.separator + base + ".xhtml"); if (xformsStream == null) { xformsStream = getInputStream(baseFolder + base + ".xhtml"); } if (xformsStream == null) { xformsStream = getInputStream(baseFolder + base.replace('.', '_') + ".xhtml"); } } if (xformsStream == null) { throw new ServletException( "Form not found for type " + base + " (template : " + StringUtils.trimToEmpty(template) + ")"); } BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(xformsStream, "UTF-8")); String line; String suffix = (pageLanguage == null) ? "" : "_" + pageLanguage; // build parameter map Map<String, String> parameters = buildParametersMap(req); while (null != ((line = bufferedReader.readLine()))) { String replaced = StringUtils.replace(line, MsgId.INT_GEN_PLACEHOLDER_SESSION_ID.getText(), sessionId + ";" + pageId); replaced = StringUtils.replace(replaced, MsgId.INT_GEN_PLACEHOLDER_CONTEXT_PATH.getText(), contextPath); // replace {@@} tokens replaced = substituteParameters(replaced, parameters); replaced = substituteUserStrings(replaced, suffix); // used ?? seem to not work very well replaced = substituteDynEnumData(replaced, initParams); xformsStringBuffer.append(replaced); } String xformsString = xformsStringBuffer.toString(); return xformsString; } protected static String substituteParameters(String toParse, Map<String, String> params) { String result = toParse; if (logger.isTraceEnabled()) { logger.trace("Parameter Token parsing :"); } // get token String regex = "\\{@@([^\\}]*)}"; Pattern patt = Pattern.compile(regex); Matcher mat = patt.matcher(toParse); while (mat.find()) { String current = mat.group(); String capture = mat.group(1); if (logger.isTraceEnabled()) { logger.trace("current :" + current); logger.trace("capture :" + capture); } String replacement = params.get(capture); if (replacement != null) { result = result.replaceAll(current.replaceAll("\\{", "\\\\{"), replacement); if (logger.isDebugEnabled()) { logger.debug("substituteParameters : " + capture + " by " + replacement); } } } return result; } protected static Map<String, String> buildParametersMap(HttpServletRequest req) { Map<String, String> map = new HashMap<String, String>(); Map<?, ?> reqp = req.getParameterMap(); for (Map.Entry<?, ?> entry : reqp.entrySet()) { Object value_ = entry.getValue(); if (entry.getKey() instanceof String && value_ instanceof String[]) { String key = (String) entry.getKey(); String value = ((String[]) entry.getValue())[0]; map.put("requests." + key, value); } } // add default parameters map.put("requests.queryString", req.getQueryString().replaceAll("&", "&")); logger.debug("parameterMap :" + map); return map; } /** * Gets an input stream for an XForms file, returning <code>null</code> if * the file does not * exist. * * @param filepath * @return */ private InputStream getInputStream(String filepath) { try { InputStream is = new FileInputStream(filepath); return is; } catch (FileNotFoundException e) { return null; } } /** * This function is for filling placeholders of dynamic enumerations with * some actual dynamic * enumeration selection data. If this can't be achieved, the concept of * having dynamic * enumerations isn't being fully exploited. The exact mechanism has not * been decided yet so we * just return the string as-is. * * @param replaced * @param initParams * @return */ private String substituteDynEnumData(String replaced, Map<String, String> initParams) { // TODO Auto-generated method stub return replaced; } /** * Returns the next identifier token from a specific position in the string. * * @param text * @param pPos * @return the token, or <code>null</code> if not a valid identifier */ private String nextIdentifierToken(String text, int pPos) { String res; char[] name = new char[256]; int idx = 0; int pos = pPos; char car = text.charAt(pos++); if (car == '.' || car == '_' || (car >= 'A' && car <= 'Z') || (car >= 'a' && car <= 'z')) { // we don't accept digits on first position while (car == '.' || car == '_' || (car >= 'A' && car <= 'Z') || (car >= 'a' && car <= 'z') || (car >= '0' && car <= '9')) { name[idx++] = car; car = text.charAt(pos++); } res = new String(name).trim(); return res; } return null; // #1672: we must detect invalid tokens } /** * Replaces all user strings with some dynamic text (currently, from the * properties file). * * @param pageText * the text in which substitutions happen * @param suffix * the language suffix, e.g. "_fr_FR" * @return the modified string */ private String substituteUserStrings(String pageText, String suffix) { String userKey; String prop; String res; String doubleShiftReplace; int pos; final char REPLACE_MARKER = '#'; // choose the right surrogate for "##" and do the replacement if (pageText.indexOf("@@") == -1) { doubleShiftReplace = "@@"; } else if (pageText.indexOf("&@") == -1) { doubleShiftReplace = "&@"; } else { doubleShiftReplace = "@&"; } String doubleMarker = "" + REPLACE_MARKER + REPLACE_MARKER; res = pageText.replaceAll(doubleMarker, doubleShiftReplace); pos = pageText.indexOf(REPLACE_MARKER); while ((pos != -1) && (pos < pageText.length())) { // compute the key userKey = nextIdentifierToken(pageText, pos + 1); if (userKey != null) { // look up key in properties file prop = MsgPool.testMsg(userKey + suffix); // if not found, look up without the suffix if (StringUtils.trimToNull(prop) == null) { prop = MsgPool.testMsg(userKey); } // // in case other sources for the value exist, like URL params, use them here // if (StringUtils.trimToNull(prop) == null) { // there is no valid replacement string found, so we make no replacement } else { res = StringUtils.replace(res, REPLACE_MARKER + userKey, prop); } } pos = pageText.indexOf(REPLACE_MARKER, pos + 1); } return res.replaceAll(doubleShiftReplace, "" + REPLACE_MARKER); } /** * Gets the controller. * * @return the controller */ private AlfrescoController getController() { return AlfrescoController.getInstance(); } /** * Inits the action. * * @param uri * the uri * @return the abstract action * @throws Exception * the exception */ private AbstractAction initAction(String uri) throws Exception { URI realUri = new URI(uri); String[] fragments = realUri.getSchemeSpecificPart().split("/"); String actionAndParams = fragments[3]; int pos = actionAndParams.indexOf('?'); if (pos == -1) { pos = actionAndParams.length(); } String actionName = actionAndParams.substring(0, pos); AbstractAction action = actions.get(actionName).newInstance(); return action; } /** * Xforms submit. * * @param result * the result * @param uri * the uri * @param submission * the submission * @param node * the node * @param chibaSessionId * the chiba session id * @throws Exception * the exception */ public void xformsSubmit(Map<String, Object> result, String uri, Submission submission, Node node, String chibaSessionId) throws Exception { logXML(node, "xformsSubmit", uri); AbstractAction action = initAction(uri); action.setProperties(getController(), uri); action.setSubmitProperties(result, submission, node, NavigationSessionListener.getServletURL(action.getSessionId())); action.submit(); } /** * Xforms resolve. * * @param uri * the uri * @return the node * @throws Exception * the exception */ public Node xformsResolve(String uri) throws Exception { log("xformsResolve", uri); Node node = null; AbstractAction action = initAction(uri); action.setProperties(getController(), uri); node = action.resolve(); logXML(node, "xformsResolve", uri); return node; } /** * Send image. * * @param req * the req * @param resp * the resp */ public void sendImage(HttpServletRequest req, HttpServletResponse resp) { String location = req.getParameter("location"); InputStream in = null; if (StringUtils.trimToNull(location) != null) { boolean isImage = false; int lastIndexOf = location.lastIndexOf('.'); if (lastIndexOf != -1) { String extension = location.substring(lastIndexOf).toUpperCase(); if (extension.endsWith("JPEG") || extension.endsWith("JPG") || extension.endsWith("PNG") || extension.endsWith("BMP") || extension.endsWith("GIF")) { isImage = true; } } if (isImage) { File sourceFile = null; if (location.startsWith("file:")) { try { // location = URLEncoder.encode(location, "UTF-8"); // URI fileURI = URI.create(location); location = location.replace("file:", ""); if (location.charAt(2) == ':') { location = location.subSequence(1, location.length()).toString(); } sourceFile = new File(location); } catch (Exception e) { sourceFile = null; if (logger.isErrorEnabled()) { logger.error(e); } } } else { sourceFile = new File(AlfrescoController.UPLOAD_DIRECTORY, location); } try { in = new FileInputStream(sourceFile); } catch (FileNotFoundException e) { if (logger.isErrorEnabled()) { logger.error(e); } } } } if (in == null) { in = NavigationSessionListener.getContext().getResourceAsStream("/resources/images/blank.png"); } ServletOutputStream out = null; try { out = resp.getOutputStream(); byte[] bytes = new byte[1024]; int bytesRead; while ((bytesRead = in.read(bytes)) != -1) { out.write(bytes, 0, bytesRead); } } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error(e); } } finally { if (in != null) { try { in.close(); } catch (IOException e) { if (logger.isErrorEnabled()) { logger.error(e); } } } if (out != null) { try { out.close(); } catch (IOException e) { if (logger.isErrorEnabled()) { logger.error(e); } } } } } }