com.bluexml.xforms.controller.alfresco.AlfrescoController.java Source code

Java tutorial

Introduction

Here is the source code for com.bluexml.xforms.controller.alfresco.AlfrescoController.java

Source

/*
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.alfresco;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Vector;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.QName;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xerces.util.XMLCatalogResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.bluexml.side.form.utils.DOMUtil;
import com.bluexml.xforms.controller.alfresco.agents.MappingAgent;
import com.bluexml.xforms.controller.alfresco.agents.SystemAgent;
import com.bluexml.xforms.controller.beans.EditNodeBean;
import com.bluexml.xforms.controller.beans.GetInstanceFormBean;
import com.bluexml.xforms.controller.beans.ListActionBean;
import com.bluexml.xforms.controller.beans.PersistFormResultBean;
import com.bluexml.xforms.controller.beans.RedirectionBean;
import com.bluexml.xforms.controller.beans.WorkflowTaskInfoBean;
import com.bluexml.xforms.controller.binding.AssociationType;
import com.bluexml.xforms.controller.navigation.FormTypeEnum;
import com.bluexml.xforms.messages.MsgId;
import com.bluexml.xforms.messages.MsgPool;
import com.google.gson.Gson;
import com.thoughtworks.xstream.XStream;

/**
 * The Class AlfrescoController.
 */
public class AlfrescoController implements AlfrescoControllerAPI {

    private static final String EMPTY_STRING = "<empty string>";

    private static final String NULL_STRING = "<null string>";

    private static final String TRACE_LOGGER_CATEGORY = "com.bluexml.xforms.controller.alfresco.AlfrescoController.trace";

    /** The upload base directory in the file system. */
    public static File UPLOAD_DIRECTORY = null;

    /** Depth of the random path where to distribute uploaded files. */
    public static int UPLOAD_DIRECTORY_RANDOM_PATH_DEPTH = 3;

    /** The upload base directory in the content management system. */
    public static String UPLOAD_REPOSITORY = null;

    /**
     * Whether file names of uploads receive a '(x)' in case the initial name
     * already exists.
     */
    public static boolean UPLOAD_REPOSITORY_APPEND = true;

    /**
     * whether info of repository uploads are formatted in the same way as
     * content information
     */
    public static boolean UPLOAD_REPOSITORY_FORMAT_INFO = false;

    /** whether to check that the form being opened matches the data id */
    public static boolean CHECK_MATCH_DATA_FORM = true;

    /** The temp directory. */
    public static File TEMP_DIRECTORY = null;

    public static int MAX_RESULTS = 50;

    /** The alfresco url. */
    public static String ALFRESCO_URL = null;

    /** The alfresco xforms url. */
    public static String ALFRESCO_XFORMS_URL = null;

    /** The default user name for transactions */
    public static String USER_NAME = null;

    /** The user password */
    public static String USER_PSWD = null;

    /**
     * The base folder for loading forms, enums, etc. Parent of 'forms',
     * 'resources, 'WEB-INF'.
     */
    public static String BASE_FOLDER = null;
    //
    //
    private static final String BLUEXML_WORKFLOW_PREFIX = "wfbx";

    private static final XStream xstream = new XStream();

    /** The logger. */
    protected static Log logger = LogFactory.getLog(AlfrescoController.class);

    /**
     * We'll use this only as a marker for trace enabled status, not as an
     * actual logger.
     */
    public static Log loggertrace = LogFactory.getLog(TRACE_LOGGER_CATEGORY);

    /** The doc builder. */
    private static DocumentBuilder docBuilder = null;

    /** The controller. */
    private static AlfrescoController instance = null;

    //
    // general settings that are persisted through sessions and users
    private static String formsPropertiesPath = null;
    private static String messagesPropertiesPath = null;
    private static String CssUrl = null;
    private static String redirectXmlPath = null;
    //

    /** whether calls to the webscript are intercepted */
    private static boolean standaloneMode = false;

    /** Stores redirection info keyed by form names */
    private static Map<String, RedirectionBean> targetTable = new HashMap<String, RedirectionBean>();

    /** The config. */
    protected Properties config = null;

    private MappingAgent mappingAgent;
    private SystemAgent systemAgent;

    static {
        // set the document builder and catalogs.
        try {
            docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            // ** #1330
            List<String> catalogs = new ArrayList<String>(8);
            URL xhtml_strict = AlfrescoController.class.getResource("/xhtml1/catalog.xml");
            catalogs.add(xhtml_strict.toString());
            XMLCatalogResolver resolver = new XMLCatalogResolver();
            resolver.setPreferPublic(true);
            resolver.setCatalogList(catalogs.toArray(new String[0]));
            docBuilder.setEntityResolver(resolver);
            // ** #1330
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error(e);
            }
            throw new RuntimeException(e);
        }

        try {
            instance = new AlfrescoController();
        } catch (Exception e1) {
            throw new RuntimeException("Failed to create the Alfresco Controller instance.", e1);
        }
        messagesPropertiesPath = null;
        formsPropertiesPath = null;
        CssUrl = null;
        standaloneMode = false;

        // statically load properties
        try {
            // loadMappingXml(); // already done by the mapping agent of the singleton instance
            loadProperties(formsPropertiesPath, messagesPropertiesPath);
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error(e);
            }
            throw new RuntimeException(e);
        }
    }

    /**
     * @return the docBuilder
     */
    public static DocumentBuilder getDocBuilder() {
        return docBuilder;
    }

    /**
     * Loads the properties files and passes them to the appropriate processor
     * for parsing.
     * 
     * @return true if both files have been loaded. If any of the files was
     *         found neither at the
     *         specified location or at the default location, returns false. If
     *         true, the files may
     *         have been loaded from default locations instead of the given
     *         paths.
     */
    public static boolean loadProperties(String formsFilePath, String messagesFilePath) {

        //
        // forms.properties
        boolean resForms;
        if (StringUtils.trimToNull(formsFilePath) != null) { // we may be setting a new path
            try {
                File theFile = new File(formsFilePath);
                InputStream stream = new FileInputStream(theFile);
                resForms = loadPropertiesFormsFromStream(stream);
                // keep the path so that a subsequent reload does not require re-giving the path
                formsPropertiesPath = formsFilePath;
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Configuration file 'forms.properties' not found at '" + formsFilePath
                            + "'. Will use defaults.", e);
                }
                resForms = loadPropertiesFormsDefault();
            }
        } else if (StringUtils.trimToNull(formsPropertiesPath) != null) { // reusing previous path
            try {
                File theFile = new File(formsPropertiesPath);
                InputStream stream = new FileInputStream(theFile);
                resForms = loadPropertiesFormsFromStream(stream);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Configuration file 'forms.properties' not found at last location "
                            + formsPropertiesPath, e);
                }
                resForms = loadPropertiesFormsDefault();
            }
        } else {
            resForms = loadPropertiesFormsDefault();
        }
        if (resForms == false) {
            return false;
        }

        // messages.properties
        InputStream streamMsgs;
        if (StringUtils.trimToNull(messagesFilePath) != null) {
            try {
                File theFile = new File(messagesFilePath);
                streamMsgs = new FileInputStream(theFile);
                messagesPropertiesPath = messagesFilePath;
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Configuration file 'messages.properties' not found at '" + messagesFilePath
                            + "'. Will use defaults.", e);
                }
                streamMsgs = loadPropertiesMessagesDefaults();
            }
        } else if (StringUtils.trimToNull(messagesPropertiesPath) != null) {
            try {
                File theFile = new File(messagesPropertiesPath);
                streamMsgs = new FileInputStream(theFile);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.error("Configuration file 'messages.properties' not found at last location "
                            + messagesPropertiesPath, e);
                }
                streamMsgs = loadPropertiesMessagesDefaults();
            }
        } else {
            streamMsgs = loadPropertiesMessagesDefaults();
        }

        if (streamMsgs == null) {
            return false;
        }
        MsgPool.setInputStream(streamMsgs);
        return true;
    }

    private static boolean loadPropertiesFormsDefault() {
        // get the default file
        URL formsURL = AlfrescoController.class.getResource("/forms.properties");
        if (formsURL == null) {
            logger.error(
                    "Configuration file 'forms.properties' not found in WEB-INF/classes. Null URL received from system.");
            return false;
        }
        try {
            File formsFile = new File(new URI(formsURL.toString()));
            InputStream stream = new FileInputStream(formsFile);
            return loadPropertiesFormsFromStream(stream);
        } catch (URISyntaxException e) {
            // I don't think this will ever be reached
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            if (logger.isErrorEnabled()) {
                logger.error("Configuration file 'forms.properties' not found in WEB-INF/classes.", e);
            }
        }
        return false;
    }

    private static InputStream loadPropertiesMessagesDefaults() {
        // get the default file
        URL msgURL = AlfrescoController.class.getResource("/messages.properties");
        if (msgURL == null) {
            if (logger.isErrorEnabled()) {
                logger.error(
                        "Configuration file 'messages.properties' not found in WEB-INF/classes. Null URL received from system.");
            }
            return null;
        }
        try {
            File formsFile = new File(new URI(msgURL.toString()));
            InputStream stream = new FileInputStream(formsFile);
            return stream;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (IOException e) {
            if (logger.isErrorEnabled()) {
                logger.error("Configuration file 'messages.properties' not found in WEB-INF/classes.", e);
            }
        }
        return null;
    }

    /**
     * Loads the properties from the given stream and initializes default
     * values.
     * 
     * @param stream
     * @return
     */
    private static boolean loadPropertiesFormsFromStream(InputStream stream) {
        Properties config = new Properties();
        try {
            config.load(stream);
            instance.initConfig(config);
            return true;
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed in loading and initializing 'forms.properties'.", e);
            }
            return false;
        }
    }

    /**
     * Gets the single instance of AlfrescoController.
     * 
     * @return single instance of AlfrescoController
     */
    public static AlfrescoController getInstance() {
        return instance;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public AlfrescoControllerAPI getController() {
        return this;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String patchDataId(String dataId) {
        String result = null;
        if (dataId != null) {
            if (!dataId.startsWith("workspace://SpacesStore/")) {
                result = "workspace://SpacesStore/" + dataId;
            } else {
                result = dataId;
            }
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String unpatchDataId(String dataId) {
        int pos = dataId.lastIndexOf('/');
        if (pos == -1) {
            return null;
        }
        return dataId.substring(pos + 1);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getAlfrescoUrl() {
        return ALFRESCO_URL;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public void setAlfrescoUrl(String alfrescoUrl) {
        ALFRESCO_URL = alfrescoUrl;
    }

    /**
     * Instantiates a new Alfresco controller.
     * 
     * @throws Exception
     */
    private AlfrescoController() throws Exception {
        super();
        // singleton

        // reference the agents
        systemAgent = new SystemAgent(this, xstream);
        mappingAgent = new MappingAgent(this);

        loadRedirectionTable(null);
    }

    //
    //
    // SystemAgent
    //
    //

    public Set<String> systemGetAllAuthoritiesAsGroupsOrUsers(AlfrescoTransaction transaction, boolean asGroups) {
        return systemAgent.getAllAuthoritiesAsGroupsOrUsers(transaction, asGroups);
    }

    public Set<String> systemGetContainingGroups(AlfrescoTransaction transaction, String userName) {
        return systemAgent.getContainingGroups(transaction, userName);
    }

    public String systemGetNodeProperty(AlfrescoTransaction transaction, NodeRef node, QName propertyName) {
        return systemAgent.getNodeProperty(transaction, node, propertyName);
    }

    public NodeRef systemGetNodeRefForUser(AlfrescoTransaction transaction, String userName) {
        return systemAgent.getNodeRefForUser(transaction, userName);
    }

    public NodeRef systemGetNodeRefForGroup(AlfrescoTransaction transaction, String groupName) {

        return systemAgent.getNodeRefForGroup(transaction, groupName);
    }

    public QName systemGetNodeType(AlfrescoTransaction transaction, String dataId) {
        return systemAgent.getNodeType(transaction, dataId);
    }

    //
    //
    // MappingAgent
    //
    //

    /**
     * @return the mappingAgent
     */
    public MappingAgent getMappingAgent() {
        return mappingAgent;
    }

    /**
     * Creates or loads the XForms instance document for a default class form.
     * 
     * @param transaction
     *            the transaction
     * @param formName
     *            the content type
     * @param dataId
     *            the node id
     * @param idAsServlet
     *            whether the request comes from a servlet
     * @return the class
     * @throws ServletException
     */
    public Document getInstanceClass(AlfrescoTransaction transaction, String formName, String dataId,
            boolean formIsReadOnly, boolean isServletRequest) throws ServletException {
        return mappingAgent.getInstanceClass(transaction, formName, dataId, formIsReadOnly, isServletRequest);
    }

    /**
     * Creates or loads the XForms instance document for a FormClass (a
     * customized form).
     * 
     * @param transaction
     *            the transaction
     * @return the form instance
     * @throws ServletException
     */
    public Document getInstanceForm(AlfrescoTransaction transaction, GetInstanceFormBean bean)
            throws ServletException {
        return mappingAgent.getInstanceForm(transaction, bean);
    }

    public Document getInstanceWorkflow(AlfrescoTransaction transaction, String formName) {
        return mappingAgent.getInstanceWorkflow(transaction, formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Document getInstanceSearch(String formName) {
        return mappingAgent.getInstanceSearch(formName);
    }

    /**
     * Gets the XForms instance document for an object.
     * 
     * @param transaction
     *            the transaction
     * @param id
     *            the id
     * @param stack
     *            the stack
     * @param isServletRequest
     * @return the element
     * @throws ServletException
     */
    public Document getObjectInstance(AlfrescoTransaction transaction, String id, Stack<AssociationType> stack,
            boolean formIsReadOnly, boolean isServletRequest) throws ServletException {
        Document alfrescoNode = readObjectFromRepository(transaction, id);

        return mappingAgent.transformAlfrescoToClassForms(transaction, alfrescoNode, stack, formIsReadOnly,
                isServletRequest);
    }

    /**
     * Reads an object form Alfresco.
     * 
     * @param transaction
     *            the transaction.
     * @param dataId
     *            the id
     * @return the document
     * @throws ServletException
     */
    public Document readObjectFromRepository(AlfrescoTransaction transaction, String dataId)
            throws ServletException {
        Map<String, String> parameters = new TreeMap<String, String>();
        parameters.put("objectId", patchDataId(dataId));
        return requestDocumentFromAlfresco(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_READ);
    }

    /**
     * Initializes configuration elements by interpreting the configuration
     * properties file.<br/>
     * NOTE: the properties file has already been read.
     * 
     * @param config
     *            the properties from the configuration file
     */
    public void initConfig(Properties config) {
        String fsPath;
        this.config = config;

        // user name and password. We don't provide hard coded defaults for these.
        USER_NAME = config.getProperty(MsgId.KEY_USER_NAME.getText());
        USER_PSWD = config.getProperty(MsgId.KEY_USER_PSWD.getText());

        // temp dir for file system uploads
        fsPath = config.getProperty(MsgId.KEY_TEMP_DIRECTORY.getText());
        if (StringUtils.trimToNull(fsPath) == null) {
            fsPath = "/tmp"; // TODO: check that this works on all platforms
        }
        TEMP_DIRECTORY = new File(fsPath);

        // file system archive folder for uploads. If none, default to predefined folder.
        fsPath = config.getProperty(MsgId.KEY_UPLOAD_DIRECTORY.getText());
        if (StringUtils.trimToNull(fsPath) == null) {
            fsPath = "/tmp/uploads"; // TODO: check on all platforms
        }
        UPLOAD_DIRECTORY = new File(fsPath);

        // repo folder for uploads, if none given, use "/app:company_home/app:user_homes"
        UPLOAD_REPOSITORY = config.getProperty(MsgId.KEY_UPLOAD_REPOSITORY.getText());
        if (StringUtils.trimToNull(UPLOAD_REPOSITORY) == null) {
            UPLOAD_REPOSITORY = "/app:company_home/app:user_homes";
        }

        // max results: default number of items in lists
        String property = config.getProperty(MsgId.KEY_MAX_RESULTS.getText());
        try {
            int value = Integer.parseInt(property);
            MAX_RESULTS = value;
        } catch (NumberFormatException e) {
            if (logger.isErrorEnabled()) {
                logger.error("Can't parse the value '" + property + "' for key '" + MsgId.KEY_MAX_RESULTS
                        + "'. Will revert to the default value.", e);
            }
            MAX_RESULTS = 50;
        }

        // whether to check matching of form vs data
        property = config.getProperty(MsgId.KEY_CHECK_MATCH_DATA_FORM.getText());
        CHECK_MATCH_DATA_FORM = !(StringUtils.equals(property, "false"));

        // whether to append ordering suffix to file names
        property = config.getProperty(MsgId.KEY_UPLOAD_REPOSITORY_APPEND.getText());
        UPLOAD_REPOSITORY_APPEND = !(StringUtils.equals(property, "false"));

        // whether to format info of repo uploads like the info of node content
        property = config.getProperty(MsgId.KEY_UPLOAD_REPOSITORY_FORMAT_INFO.getText());
        UPLOAD_REPOSITORY_FORMAT_INFO = StringUtils.equals(property, "true");

        // depth of the random path
        property = config.getProperty(MsgId.KEY_UPLOAD_DIR_PATH_DEPTH.getText());
        try {
            int depth = Integer.parseInt(property);
            UPLOAD_DIRECTORY_RANDOM_PATH_DEPTH = depth;
        } catch (NumberFormatException e) {
            if (logger.isErrorEnabled()) {
                logger.error("Can't parse the value '" + property + "' for key '" + MsgId.KEY_UPLOAD_DIR_PATH_DEPTH
                        + "'. Will revert to the default value.", e);
            }
            UPLOAD_DIRECTORY_RANDOM_PATH_DEPTH = 3;
        }

        checkDirectoryExists(UPLOAD_DIRECTORY, true);
        checkDirectoryExists(TEMP_DIRECTORY, false);

        ALFRESCO_URL = config.getProperty(MsgId.KEY_ALFRESCO_URL.getText());
        ALFRESCO_XFORMS_URL = ALFRESCO_URL + "/service/xforms/";

        BASE_FOLDER = getDefaultBaseFolder();
    }

    /**
     * Checks whether a directory (from the configuration file) exists. If not,
     * attempts to create
     * the paths. If that fails, creates a path under the webapp's folder.
     * 
     * @param file
     *            the file
     * @param isUploadDir
     */
    private void checkDirectoryExists(File file, boolean isUploadDir) {
        if (!file.exists()) {
            if (logger.isErrorEnabled()) {
                logger.error(file.getAbsolutePath() + " doesn't exist. Will try to create the path.");
            }
            if (file.mkdirs() == false) {
                String dirName = isUploadDir ? "upload" : "temp";
                if (logger.isErrorEnabled()) {
                    logger.error("Couldn't create " + file.getAbsolutePath() + ". Will default to '" + dirName
                            + "' under the webapp's current folder.");
                }
                // we need to create the directory under the webapp's folder
                String dirPath = getDefaultBaseFolder();
                dirPath += File.separator + dirName;
                boolean result;
                if (isUploadDir) {
                    UPLOAD_DIRECTORY = new File(dirPath);
                    result = UPLOAD_DIRECTORY.mkdirs();
                } else {
                    TEMP_DIRECTORY = new File(dirPath);
                    result = TEMP_DIRECTORY.mkdirs();
                }
                if (result == false) {
                    if (logger.isErrorEnabled()) {
                        logger.error(
                                "Couldn't create directory " + dirPath + ". Uploads will not perform correctly.");
                    }
                }
            }
        }
    }

    /**
     * Gets the complete filesystem path to the default base folder (which
     * contains the 'forms' and
     * 'WEB-INF') folders.
     * 
     * @return
     */
    private String getDefaultBaseFolder() {
        String dirPath = null;
        try {
            File mappingFile = null;
            URL url = AlfrescoController.class.getResource("/mapping.xml");
            mappingFile = new File(new URI(url.toString()));
            dirPath = mappingFile.getAbsolutePath();
            // @Amenel: I am not comfortable with the assumption about the path to
            // mapping.xml: although true today, it may not be so in the future, depending
            // on OS or JVM.
            String subPath = File.separator + "WEB-INF" + File.separator + "classes" + File.separator
                    + "mapping.xml";
            // subPath is "/WEB-INF/classes/mapping.xml" on Mac OS, Unix, Linux
            // subPath is "\WEB-INF\classes\mapping.xml" on Windows
            dirPath = StringUtils.replace(dirPath, subPath, "");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return dirPath;
    }

    /**
     * Returns the directory for uploading files into the file system. If none
     * given in initParams
     * or the directory does not exist, falls back to the value from
     * configuration file.
     * 
     * @param initParams
     */
    public File getParamUploadPathInFileSystem(Map<String, String> initParams) {

        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_UPLOAD_DIRECTORY.getText());
        }
        if (StringUtils.trimToNull(result) == null) {
            return UPLOAD_DIRECTORY;
        }
        File resFile = new File(result);
        return resFile.exists() ? resFile : UPLOAD_DIRECTORY;
    }

    /**
     * Returns a path for uploading files into the repository. If none given in
     * initParams, falls
     * back to the value from the configuration file.
     * 
     * @param initParams
     */
    public String getParamUploadPathInRepository(Map<String, String> initParams) {

        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_UPLOAD_REPOSITORY.getText());
        }
        return (result != null) ? result : UPLOAD_REPOSITORY;
    }

    //
    // for the next functions, see MsgId.
    //
    public String getParamBaseFolder(Map<String, String> initParams) {
        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_BASE_FOLDER.getText());
        }
        return (StringUtils.trimToNull(result) == null) ? BASE_FOLDER : result;
    }

    public boolean getParamUploadRepoAppendSuffix(Map<String, String> initParams) {
        String paramStr = null;
        if (initParams != null) {
            paramStr = initParams.get(MsgId.PARAM_UPLOAD_REPOSITORY_APPEND.getText());
        }
        if (StringUtils.trimToNull(paramStr) != null) {
            return !(StringUtils.equals(paramStr, "false"));
        }
        return UPLOAD_REPOSITORY_APPEND;
    }

    public boolean getParamUploadRepoFormatInfo(Map<String, String> initParams) {
        String paramStr = null;
        if (initParams != null) {
            paramStr = initParams.get(MsgId.PARAM_UPLOAD_REPOSITORY_FORMAT.getText());
        }
        if (StringUtils.trimToNull(paramStr) != null) {
            return !(StringUtils.equals(paramStr, "false"));
        }
        return UPLOAD_REPOSITORY_FORMAT_INFO;
    }

    /**
     * This method check the parameter 'checkMatchDF' in the request.
     * Use 'checkMatchDF=false' in the case of form Workflow which are not
     * associated to Form class in order to avoid the various check of data type
     * 
     * @param initParams
     *            the request parameters
     * @return true if data type check necessary, false otherwise (form workflow
     *         not associated to form class)
     */
    public boolean getParamCheckMatchDataForm(Map<String, String> initParams) {
        String paramStr = null;
        if (initParams != null) {
            paramStr = initParams.get(MsgId.PARAM_CHECK_MATCH_DATA_FORM.getText());
        }
        if (StringUtils.trimToNull(paramStr) != null) {
            return !(StringUtils.equals(paramStr, "false"));
        }
        return CHECK_MATCH_DATA_FORM;
    }

    public String getParamUserName(Map<String, String> initParams) {
        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_USER_NAME.getText());
        }
        return (StringUtils.trimToNull(result) == null) ? USER_NAME : result;
    }

    public String getParamUserPswd(Map<String, String> initParams) {
        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_USER_PSWD.getText());
        }
        return (StringUtils.trimToNull(result) == null) ? USER_PSWD : result;
    }

    public int getParamMaxResults(Map<String, String> initParams) {
        int result = MAX_RESULTS;
        if (initParams != null) {
            String resultStr = initParams.get(MsgId.PARAM_MAX_RESULTS.getText());
            if (StringUtils.trimToNull(resultStr) != null) {
                try {
                    result = Integer.parseInt(resultStr);
                } catch (NumberFormatException e) {
                    if (logger.isErrorEnabled()) {
                        logger.error("Can't parse the value '" + resultStr + "' for parameter '"
                                + MsgId.PARAM_MAX_RESULTS + "'. Will revert to the previous value.", e);
                    }
                    result = MAX_RESULTS;
                }
            }
        }
        return result;
    }

    /**
     * Provides the number of directory levels into which filesystem uploads
     * should be randomly
     * distributed.
     * 
     * @param initParams
     * @return
     */
    public int getParamUploadPathDepth(Map<String, String> initParams) {

        String result = null;
        if (initParams != null) {
            result = initParams.get(MsgId.PARAM_UPLOAD_DEPTH.getText());
        }
        if (StringUtils.trimToNull(result) == null) {
            return UPLOAD_DIRECTORY_RANDOM_PATH_DEPTH;
        }
        try {
            return Integer.parseInt(result);
        } catch (NumberFormatException e) {
            return UPLOAD_DIRECTORY_RANDOM_PATH_DEPTH;
        }
    }

    /**
     * Persists a search form: collects the search info on search forms.
     * 
     * @param transaction
     * @param instance
     * @param initParams
     * @param isServletRequest
     * @return
     * @throws ServletException
     */
    public String persistSearch(String formName, Node instance, boolean shortPropertyNames,
            Map<String, String> initParams) throws ServletException {
        return mappingAgent.persistSearch(formName, instance, shortPropertyNames, initParams);
    }

    /**
     * Persists a class form: transforms the XForms instance into a
     * GenericClass, and either saves
     * or updates a node.
     * 
     * @param instance
     *            the instance
     * @param transaction
     *            the transaction
     * @param isServletRequest
     * @param initParams
     * @return the object id as given by the transaction manager
     * @throws ServletException
     */
    public PersistFormResultBean persistClass(AlfrescoTransaction transaction, Node instance,
            boolean isServletRequest, Map<String, String> initParams) throws ServletException {
        return mappingAgent.persistClass(transaction, instance, isServletRequest, initParams);
    }

    /**
     * Persists a FormClass: transforms the XForms instance into a GenericClass,
     * and either saves or
     * updates a node.
     * 
     * @param type
     *            the type
     * @param instance
     *            the instance
     * @param transaction
     *            the transaction
     * @param initParams
     * @param isMassTagging
     * @return the temporary id assigned to the class
     * @throws ServletException
     */
    public PersistFormResultBean persistForm(AlfrescoTransaction transaction, String type, Node instance,
            Map<String, String> initParams, boolean isMassTagging) throws ServletException {
        return mappingAgent.persistForm(transaction, type, instance, initParams, isMassTagging);
    }

    /**
     * Returns the data on the form in a specific JSON format. This is to be
     * used with forms other
     * than FormSearch's when called in search mode. If the form is a
     * FormSearch, use
     * {@link #persistSearch(AlfrescoTransaction, Node, boolean)}
     * 
     * @param transaction
     * @param formName
     * @param instance
     * @param initParams
     * @return a JSON string
     * @throws ServletException
     */
    public String persistFormJSON(AlfrescoTransaction transaction, String formName, Node instance,
            boolean shortPropertyNames, Map<String, String> initParams) throws ServletException {
        return mappingAgent.persistFormToJSON(transaction, formName, instance, shortPropertyNames, initParams);
    }

    /**
     * Builds a GenericClass object from the instance of a workflow and
     * processes the possible
     * upload fields.
     * 
     * @param transaction
     * @param taskTypeName
     *            the id of the workflow form
     * @param taskElt
     *            the root element containing the workflow properties
     * @param initParams
     * @return
     * @throws ServletException
     */
    public PersistFormResultBean persistWorkflow(AlfrescoTransaction transaction, String taskTypeName, Node taskElt,
            Map<String, String> initParams) throws ServletException {
        return mappingAgent.persistWorkflow(transaction, taskTypeName, taskElt, initParams);
    }

    public void executeBatch(AlfrescoTransaction alfrescoTransaction) throws ServletException {
        Map<String, String> parameters = new TreeMap<String, String>();
        parameters.put("datas", MappingAgent.marshalBatch(alfrescoTransaction));

        Map<String, String> ids = new HashMap<String, String>();

        Document result = requestDocumentFromAlfresco(alfrescoTransaction, parameters,
                MsgId.INT_WEBSCRIPT_OPCODE_BATCH);
        if (result == null) {
            throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
        }
        Element idsElement = result.getDocumentElement();
        NodeList childNodes = idsElement.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node idNode = childNodes.item(i);
            if (idNode instanceof Element) {
                Element idElement = (Element) idNode;
                String to = null;
                String from = null;
                NodeList idChildNodes = idElement.getChildNodes();
                for (int j = 0; j < idChildNodes.getLength(); j++) {
                    Node node = idChildNodes.item(j);
                    if (node instanceof Element) {
                        Element element = (Element) node;
                        if (StringUtils.equals(element.getTagName(), "from")) {
                            from = StringUtils.trim(element.getTextContent());
                        }
                        if (StringUtils.equals(element.getTagName(), "to")) {
                            to = StringUtils.trim(element.getTextContent());
                        }
                    }
                }
                ids.put(from, to);
            }
        }

        alfrescoTransaction.setIds(ids);
    }

    /**
     * Gets the captions.
     * 
     * @param ids
     *            the ids
     * @param transaction
     *            the transaction
     * @return the captions
     * @throws ServletException
     */
    @SuppressWarnings("all")
    public List<String> getCaptions(AlfrescoTransaction transaction, List<String> ids) throws ServletException {
        Map<String, String> parameters = new TreeMap<String, String>();
        parameters.put("query", StringUtils.join(ids, ";"));
        Document requestDocument = requestDocumentFromAlfresco(transaction, parameters,
                MsgId.INT_WEBSCRIPT_OPCODE_LABELS);
        if (requestDocument == null) {
            throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
        }

        List<String> result = new ArrayList<String>();
        // List elements = DOMUtil.getChildElements(requestDocument.getDocumentElement());
        List<Element> elements = DOMUtil.getAllChildren(requestDocument.getDocumentElement());
        for (Element element : elements) {
            result.add(DOMUtil.getChild(element, "value").getTextContent());
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public String getEnumCaption(AlfrescoTransaction transaction, String code) throws ServletException {
        Map<String, String> parameters = new TreeMap<String, String>();
        parameters.put("query", "enum;" + code);
        Document requestDocument = requestDocumentFromAlfresco(transaction, parameters,
                MsgId.INT_WEBSCRIPT_OPCODE_LABELS);
        if (requestDocument == null) {
            throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
        }

        String result = null;
        List elements = DOMUtil.getAllChildren(requestDocument.getDocumentElement());
        if (elements.size() > 0) {
            Object object = elements.get(0);
            Element element = (Element) object;
            result = DOMUtil.getChild(element, "value").getTextContent();
        }
        return result;
    }

    /**
     * Gets the enum.
     * 
     * @param transaction
     *            the transaction
     * @param type
     *            the type
     * @param filterParent
     *            the filter parent
     * @param filterData
     *            the filter data
     * @param query
     * @return the enum
     * @throws ServletException
     */
    public Node getDynamicEnum(AlfrescoTransaction transaction, String type, String filterParent, String filterData,
            String query, boolean limit) throws ServletException {
        Map<String, String> parameters = new TreeMap<String, String>();
        String fp = StringUtils.trimToNull(filterParent);
        if (fp != null) {
            parameters.put("parent", fp);
        }
        String fd = StringUtils.trimToNull(filterData);
        if (fd != null) {
            parameters.put("context", fd);
        }
        String q = StringUtils.trimToNull(query);
        if (q != null) {
            parameters.put("query", q);
        }
        if (limit) {
            parameters.put("limit", "true");
        }

        parameters.put("type", mappingAgent.getEnumTypeName(type));
        Document reqDoc = requestDocumentFromAlfresco(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_ENUM);
        if (reqDoc == null) {
            throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
        }
        Element docElt = reqDoc.getDocumentElement();

        Element queryElement = reqDoc.createElement("query");
        queryElement.setTextContent(StringUtils.trimToEmpty(query));
        docElt.appendChild(queryElement);
        docElt.appendChild(reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDID.getText()));
        docElt.appendChild(reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDLABEL.getText()));
        return reqDoc;
    }

    /**
     * Gets the result set for the list action by calling the webscript. This
     * function is called
     * when a form is loaded (to provide the initial item set), and each time a
     * search is launched,
     * whether by character input or by a "launch search" button.
     * 
     * @param transaction
     *            the transaction
     * @param bean
     *            a bean for parameters, will allow us to extend the parameter
     *            set without changing
     *            the signature
     * @return the list document
     * @throws ServletException
     */
    public Node getList(AlfrescoTransaction transaction, ListActionBean bean) throws ServletException {
        // The use of trimToEmpty is for backward compatibility.
        String dataType = bean.getDataType();
        String query = bean.getQuery();
        String maxResults = bean.getMaxResults();
        String format = StringUtils.trimToEmpty(bean.getFormat());
        String maxLength = bean.getMaxLength();
        String identifier = StringUtils.trimToEmpty(bean.getIdentifier());
        String filterAssoc = StringUtils.trimToEmpty(bean.getFilterAssoc());
        String compositionStatus = StringUtils.trimToEmpty(bean.getCompositionStatus());
        String searchModeStatus = StringUtils.trimToEmpty(bean.getSearchMode());
        String luceneQuery = StringUtils.trimToEmpty(bean.getLuceneQuery());
        String dataSourceURI = StringUtils.trimToNull(bean.getDataSourceURI());

        Map<String, String> parameters = new TreeMap<String, String>();

        String type = null;
        /*
         * maxSize fixe le nombre d'lements  afficher dans le widget, par
         * dfaut, MAX_RESULTS. Ce
         * nbre sera rinitialis par le bouton 'Tout'. Valeurs possibles:
         * MAX_RESULTS ("50" ou
         * celle indique ds forms.properties) ou fix par la proprit 'field
         * size' dans le
         * modeleur. Dans ce cas, SELECTMAX conserve tjrs la valeur de field
         * size.
         */
        String maxSize = mappingAgent.getFieldSizeForField(dataType,
                "" + getParamMaxResults(transaction.getInitParams()), transaction.getFormId());
        type = (identifier == null ? mappingAgent.getClassTypeAlfrescoName(dataType) : dataType);

        // always provided in the URI
        parameters.put("type", type);

        parameters.put("format", format);

        // always provided in the URI
        parameters.put("maxLength", maxLength);

        parameters.put("identifier", identifier);
        parameters.put("filterAssoc", filterAssoc);

        // always provided in the URI.
        parameters.put("isComposition", compositionStatus);

        // always provided in the URI.
        parameters.put("isSearchMode", searchModeStatus);
        parameters.put("luceneQuery", luceneQuery);

        String q = StringUtils.trimToNull(query);
        if (q != null) {
            parameters.put("query", q);
        }

        if (StringUtils.trimToNull(maxResults) != null) {
            parameters.put("maxResults", maxResults);
        } else {
            parameters.put("maxResults", maxSize);
        }

        Document reqDoc;
        if (isInStandaloneMode()) {
            // NOTE: we use the maxLength for indicating the number of items in the list!
            reqDoc = requestDummyDocumentList(type, maxLength, searchModeStatus);
        } else {

            if (dataSourceURI == null) {
                reqDoc = requestDocumentFromAlfresco(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_LIST);
            } else {
                // do not use default Xform webscript uri
                reqDoc = requestDocumentFromAlfresco(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_LIST,
                        dataSourceURI);
            }

            // ** #1234
            if (reqDoc == null) {
                if (logger.isErrorEnabled()) {
                    logger.error("The Alfresco server is unavailable. Returning a dummy list.");
                }
                // setStandaloneMode(true);
                reqDoc = requestDummyDocumentList(type, maxLength, searchModeStatus);
            }
            // ** #1234
        }
        Element docElement = reqDoc.getDocumentElement();

        docElement.appendChild(reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDID.getText()));
        docElement.appendChild(reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDLABEL.getText()));
        docElement.appendChild(reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDTYPE.getText()));

        Element maxResultsElement = reqDoc.createElement(MsgId.INT_INSTANCE_SELECTEDMAX.getText());
        maxResultsElement.setTextContent(maxSize);
        docElement.appendChild(maxResultsElement);

        return reqDoc;

    }

    /**
     * Returns a fake list built without contacting Alfresco.
     * 
     * @param alfrescoName
     *            the datatype
     * @param size
     *            the number of items in the list (will be overridden if search
     *            mode)
     * @param searchModeStatus
     *            "1" if the widget is a search instead of a filter
     * @return
     */
    private Document requestDummyDocumentList(String alfrescoName, String size, String searchModeStatus) {
        Document doc = docBuilder.newDocument();
        int nb = Integer.parseInt(size);
        if (nb == 0) {
            nb = 10;
        }
        if (StringUtils.equals(searchModeStatus, "1")) { // override
            nb = 0;
        }

        Element root = doc.createElement("results");

        Element query = doc.createElement("query");
        Element count = doc.createElement("count");
        count.setTextContent("" + nb);

        Element maxResults = doc.createElement("maxResults");
        maxResults.setTextContent("0");

        Element returned = doc.createElement("returned");
        returned.setTextContent("" + nb);

        Element filteredOut = doc.createElement("filteredOut");
        filteredOut.setTextContent("0");

        Element typeFound = doc.createElement("typeFound");
        typeFound.setTextContent("simulated");

        Element subquery = doc.createElement("query");
        query.appendChild(count);
        query.appendChild(maxResults);
        query.appendChild(returned);
        query.appendChild(filteredOut);
        query.appendChild(typeFound);
        query.appendChild(subquery);

        root.appendChild(query);

        for (int i = 0; i < nb; i++) {
            Element item = doc.createElement(MsgId.INT_INSTANCE_SELECT_ITEM.getText());
            Element id = doc.createElement(MsgId.INT_INSTANCE_SELECT_ID.getText());
            id.setTextContent("" + i);
            Element label = doc.createElement(MsgId.INT_INSTANCE_SELECT_LABEL.getText());
            label.setTextContent(alfrescoName + "_" + i);
            Element qname = doc.createElement(MsgId.INT_INSTANCE_SELECT_TYPE.getText());
            qname.setTextContent("type_" + alfrescoName + "_" + i);

            item.appendChild(id);
            item.appendChild(label);
            item.appendChild(qname);

            root.appendChild(item);
        }
        doc.appendChild(root);
        return doc;
    }

    /**
     * Provides a bridge to the webscript, returning a String. //$$ NON-API
     * 
     * @param parameters
     *            the parameters
     * @param opCode
     *            the opCode
     * @param transaction
     *            the transaction
     * @return the string
     * @throws ServletException
     */
    public String requestString(AlfrescoTransaction transaction, Map<String, String> parameters, MsgId opCode)
            throws ServletException {
        String result = null;
        try {
            PostMethod post = requestPost(transaction, parameters, opCode);
            result = StringUtils.trim(post.getResponseBodyAsString());
        } catch (ConnectException e) {
            throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
        } catch (IOException e) {
            if (logger.isErrorEnabled()) {
                logger.error("Caught exception while requesting string from Alfresco", e);
            }
            throw new ServletException(MsgPool.getMsg(MsgId.MSG_ERROR_DEFAULT_MSG));
        }
        if (result != null && result.startsWith("<exception>")) {
            throw new AlfrescoWebscriptException(result);
        }
        return result;
    }

    /**
     * @param transaction
     * @param parameters
     * @param opCode
     * @return
     * @throws ServletException
     */
    private Document requestDocumentFromAlfresco(AlfrescoTransaction transaction, Map<String, String> parameters,
            MsgId opCode) throws ServletException {
        return requestDocumentFromAlfresco(transaction, parameters, opCode, null);
    }

    /**
     * Provides a bridge to the webscript, returning a Document.
     * 
     * @param transaction
     *            the transaction
     * @param parameters
     *            the parameters
     * @param opCode
     *            the opCode
     * @param dataSourceURI
     * @return the document
     * @throws ServletException
     */
    private Document requestDocumentFromAlfresco(AlfrescoTransaction transaction, Map<String, String> parameters,
            MsgId opCode, String dataSourceURI) throws ServletException {
        Document result = null;
        PostMethod post;

        // request the document from the webscript @ Alfresco
        try {
            post = requestPost(transaction, parameters, opCode, dataSourceURI);
        } catch (ConnectException e) { // #1234
            if (!isInDebugMode()) {
                throw new ServletException(MsgId.INT_MSG_ALFRESCO_SERVER_DOWN.getText());
            }
            return null;
        } catch (ServletException se) {
            throw se;
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Caught exception while requesting document from Alfresco", e);
            }
            throw new ServletException(MsgPool.getMsg(MsgId.MSG_ERROR_DEFAULT_MSG));
        }

        // parse the result into a document
        try {

            if (logger.isDebugEnabled()) {
                String response = post.getResponseBodyAsString();
                logger.debug(response);
            }

            result = synchronizedParse(post.getResponseBodyAsStream());
        } catch (IOException e) {
            logger.error("Exception while parsing the document requested from Alfresco", e);
            throw new ServletException(MsgPool.getMsg(MsgId.MSG_ERROR_DEFAULT_MSG));
        } catch (SAXException e) {
            logger.error("Exception while parsing the document requested from Alfresco", e);
            throw new ServletException(MsgPool.getMsg(MsgId.MSG_ERROR_DEFAULT_MSG));
        }
        if (result != null) {
            if (StringUtils.equalsIgnoreCase("exception", result.getDocumentElement().getTagName())) {
                throw new AlfrescoWebscriptException(result, transaction);
            }
        }
        return result;
    }

    /**
     * @param is
     *            the input stream to parse from
     * @return the document built from the stream
     * @throws IOException
     * @throws SAXException
     */
    // #1227
    synchronized public Document synchronizedParse(InputStream is) throws IOException, SAXException {
        return docBuilder.parse(is);
    }

    /**
     * @param transaction
     * @param parameters
     * @param opCode
     * @return
     * @throws IOException
     * @throws ServletException
     */
    private PostMethod requestPost(AlfrescoTransaction transaction, Map<String, String> parameters, MsgId opCode)
            throws IOException, ServletException {
        return requestPost(transaction, parameters, opCode, null);
    }

    /**
     * Request post. Bridge to our XForms webscript under Alfresco. //$$ TRACE
     * LOG
     * 
     * @param transaction
     *            the transaction. MANDATORY and NEVER <code>null</code>
     * @param parameters
     *            the parameters
     * @param opCode
     *            the code for the webscript operation being called.
     * @param dataSourceURI
     * @return the post method
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws HttpException
     *             the http exception
     * @throws ServletException
     */
    private PostMethod requestPost(AlfrescoTransaction transaction, Map<String, String> parameters, MsgId opCode,
            String dataSourceURI) throws IOException, ServletException {
        //
        // security: enforce use of possible access controls on the Alfresco side.
        if (transaction == null) {
            logger.error("Null transaction.");
            throw new ServletException("A transaction is required for webscript requests.");
        }
        String legitimateLogin = transaction.getLogin();
        if (legitimateLogin == null) {
            legitimateLogin = getParamUserName(transaction.getPage().getInitParams());
        }
        if (StringUtils.trimToNull(legitimateLogin) == null) {
            logger.error("No user name in the transaction.");
            throw new ServletException(
                    "Cannot complete the action: a user name is required for webscript requests.");
        }

        //
        // log some info
        if (loggertrace.isTraceEnabled()) {
            logger.trace("Calling the webscript for user '" + legitimateLogin + "' with request: " + opCode);
            logger.trace("Parameters : ");
            Set<Entry<String, String>> entrySet2 = parameters.entrySet();
            for (Entry<String, String> entry2 : entrySet2) {
                String value = entry2.getValue();
                if (value == null) {
                    value = NULL_STRING;
                } else if (value.equals("")) {
                    value = EMPTY_STRING;
                }
                logger.trace("  " + entry2.getKey() + " = " + value);
            }
        }

        // set url
        String url;
        boolean useDataSource = dataSourceURI != null && dataSourceURI.startsWith("http");
        if (useDataSource) {
            // it's possible that url have parameters so to avoid conflic with sidereader parameters '&' is replaced by '#'         
            url = dataSourceURI.replaceAll("@\\$@", "&");
        } else {
            url = ALFRESCO_XFORMS_URL + opCode;
        }
        PostMethod post = new PostMethod(url);
        if (!useDataSource) {
            Set<Entry<String, String>> entrySet = parameters.entrySet();
            for (Entry<String, String> entry : entrySet) {
                post.setParameter(entry.getKey(), entry.getValue());
            }
            // if (StringUtils.trimToNull(transaction.getLogin()) == null) {
            // post.setParameter("username", getParamLoginUserName(transaction.getInitParams()));
            // } else {
            // post.setParameter("username", transaction.getLogin());
            // }
            post.setParameter("username", transaction.getLogin());

            post.setParameter("serviceCallerId", "XFormsController");
            post.setParameter("serviceCallerVersion", "2.0.0");
        } else {
            Gson gson = new Gson();
            String json = gson.toJson(parameters);
            post.setParameter("xformsParameters", json);
        }

        post.addRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE + "; charset=UTF-8");

        executeMethod(post, false);

        if (loggertrace.isTraceEnabled()) {
            logger.trace("Response : ");
            String responseBodyAsString = post.getResponseBodyAsString();
            if (responseBodyAsString == null) {
                logger.trace(NULL_STRING);
            } else if (responseBodyAsString.equals("")) {
                logger.trace(EMPTY_STRING);
            } else {
                logger.trace(responseBodyAsString);
            }
        }

        return post;
    }

    /**
     * Executes an HTTP method.
     * 
     * @param method
     *            the method
     * @param hasTicket
     *            the has ticket
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws HttpException
     *             the http exception
     */
    private void executeMethod(HttpMethodBase method, boolean hasTicket) throws IOException, HttpException {
        HttpClient client = new HttpClient();
        client.executeMethod(method);
    }

    /**
     * Enqueues a delete operation for the current transaction.
     * 
     * @param nodeRef
     *            the node ref
     * @param transaction
     *            the transaction
     */
    public void delete(AlfrescoTransaction transaction, String nodeRef) {
        transaction.queueDelete(nodeRef);
    }

    /**
     * Removes the reference.
     * 
     * @param node
     *            the node
     * @param elementId
     *            the element id
     */
    public void removeReference(Document node, String elementId) {
        mappingAgent.removeReference(node, elementId);
    }

    /**
     * Checks whether a datatype has sub types.
     * 
     * @param dataType
     *            the data type
     * @return true, if successful
     */
    public boolean hasSubTypes(String dataType) {
        return mappingAgent.hasSubTypes(dataType);
    }

    /**
     * Checks if is an enumeration is dynamic.
     * 
     * @param type
     *            the type
     * @return true, if is dynamic enum
     */
    public boolean isDynamicEnum(String type) {
        return mappingAgent.isDynamicEnum(type);
    }

    /**
     * Gets the label under which an association is displayed on a specific
     * form. The label is
     * indicated for the model choice field in the modeler.
     * 
     * @param completeAssoName
     * @param dataType
     *            the form id, i.e. a class name or form name in the mapping.xml
     *            file
     * @return
     */
    public String getShortAssociationName(String completeAssoName, String dataType) {
        return mappingAgent.getDisplayLabelFromAssociationName(completeAssoName, dataType);
    }

    /**
     * Extracts the id to be edited from an XForms instance. The DOM node
     * providing that id must be
     * reset (i.e. emptied) so that subsequent editions can happen correctly on
     * the same form.
     * 
     * @param node
     * @return the id, or null (this latter case should not happen if the Edit
     *         button's action of
     *         setting the <edit id> in the instance is done correctly).
     */
    public EditNodeBean getEditNodeAndReset(Node node) {
        Element rootElt = ((Document) node).getDocumentElement();
        // find the edit id element
        Element editIdElt = DOMUtil.getElementInDescentByNameNonNull(rootElt,
                MsgId.INT_INSTANCE_SIDEEDIT.getText());
        if (editIdElt != null) {
            String id = editIdElt.getTextContent();
            editIdElt.setTextContent(null); // <- reset the id. MANDATORY.

            // get the data type // #1510
            String dataType = null;
            try {
                Element parent = (Element) editIdElt.getParentNode();
                Element typeElt = DOMUtil.getChild(parent, MsgId.INT_INSTANCE_SIDETYPE.getText());
                if (typeElt != null) {
                    dataType = typeElt.getTextContent();
                }
            } catch (Exception e) {
                // nothing to do
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Getting edit id, found: '" + id + "' with data type: '" + dataType + "'");
            }
            return new EditNodeBean(id, dataType);
        }
        if (logger.isErrorEnabled()) {
            logger.error("No id found for node edition.");
        }
        return null;
    }

    //
    // additions for workflows
    //
    /**
     * Starts a workflow and returns the instance id.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param workflowDefinitionId
     * @param attributes
     *            Qualified Alfresco Attributes
     * @return the id of the newly created workflow instance, or
     *         <code>null</code> if problem.
     */
    public String workflowStart(AlfrescoTransaction transaction, String workflowDefinitionId,
            Map<QName, Serializable> attributes) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("wfName", workflowDefinitionId);
        parameters.put("attributes", attributes);
        return (String) workflowRequestCall(transaction, "wfStart", parameters);
    }

    /**
     * Ends a task by following the given transition.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param taskId
     *            Task to update
     * @param transitionId
     *            the id of the transition to follow
     * @return the id of the task, or <code>null</code> if a problem occurred
     */
    public boolean workflowEndTask(AlfrescoTransaction transaction, String taskId, String transitionId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("taskId", taskId);
        parameters.put("transitionId", transitionId);
        Boolean result = (Boolean) workflowRequestCall(transaction, "wfEnd", parameters);
        if (result == null) {
            return false;
        }
        return result;
    }

    /**
     * Updates a task with the given properties.
     * 
     * @param transaction
     * @param taskId
     * @param properties
     * @return
     */
    public boolean workflowUpdateTask(AlfrescoTransaction transaction, String taskId,
            Map<QName, Serializable> properties) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("taskId", taskId);
        parameters.put("properties", properties);
        Boolean result = (Boolean) workflowRequestCall(transaction, "wfUpdate", parameters);
        if (result == null) {
            return false;
        }
        return result;
    }

    /**
     * Cancels a workflow.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param workflowId
     *            Id of the workflow to cancel
     * @return The updated instance
     */
    public WorkflowInstance workflowCancel(AlfrescoTransaction transaction, String workflowId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("workflowId", workflowId);
        return (WorkflowInstance) workflowRequestCall(transaction, "wfCancel", parameters);
    }

    /**
     * Collects all non empty properties available on an instanceId.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param instanceId
     *            the workflow instance Id
     * @return
     */
    @SuppressWarnings("unchecked")
    private Map<QName, Serializable> workflowCollectInstanceProperties(AlfrescoTransaction transaction,
            String instanceId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("workflowId", instanceId);

        return (Map<QName, Serializable>) workflowRequestCall(transaction, "wfCollectInstanceProperties",
                parameters);
    }

    /**
     * Gets selected pieces of information about the currently active tasks for
     * the given instance.
     * Each item of the list is a string in which the pieces of info are
     * concatenated and separated
     * using a separator string defined in {@link XFormsWork.wfGetCurrentTasks}.
     * e.g. (with
     * SEPARATOR="{::}":
     * 
     * "jbpm$64{::}wfbxDigitizationProcess:Debut{::}Demarrage de la dematerialisation"
     * )
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param instanceId
     *            the id of a currently live workflow instance
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<String> workflowGetCurrentTasksInfo(AlfrescoTransaction transaction, String instanceId) { // #1534
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("workflowId", instanceId);

        return (List<String>) workflowRequestCall(transaction, "wfGetCurrentTasksIds", parameters);
    }

    /**
     * Gets the id for the latest version of a process.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param processName
     *            the workflow definition name
     * @return
     */
    public String workflowGetIdForProcessDefinition(AlfrescoTransaction transaction, String processName) { // #1534
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("processName", processName);

        return (String) workflowRequestCall(transaction, "wfGetIdForProcessDefinition", parameters);
    }

    /**
     * Gets the set of properties qualified names a for a task of a process
     * definition.<br/>
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param processId
     *            the workflow definition name, e.g. "jbpm$105"
     * @param taskName
     *            the name/id of the task, e.g. "wfbxDigitizationProcess:Debut"
     * @return the set, or <code>null</code> if either of the ids was not found
     */
    @SuppressWarnings("unchecked")
    public List<String> workflowGetTaskPropertiesQNames(AlfrescoTransaction transaction, String processId,
            String taskName) { // #1534
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("processId", processId);
        parameters.put("taskId", taskName);

        return (List<String>) workflowRequestCall(transaction, "wfGetTaskDefinitionPropertyQNames", parameters);
    }

    /**
     * Creates a workflow package under the default package location and adds an
     * existing content to
     * that workflow package so that a call to
     * WorkflowService.getWorkflowsForContent for that
     * content links to the workflow for which the created package is the
     * package.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @return the new package
     * @throws ServletException
     */
    public NodeRef workflowCreatePackage(AlfrescoTransaction transaction, String nodeToAdd)
            throws ServletException {

        Map<String, String> parameters = new HashMap<String, String>();
        if (nodeToAdd != null) { // #1284: workflows without attached data form
            parameters.put("content", nodeToAdd);
        }
        // parameters.put("package", (String) null); // do not set a null value

        String resultId = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_PACKAGE);

        if (StringUtils.trimToNull(resultId) != null) {
            return (NodeRef) xstream.fromXML(resultId);
        }
        return null;
    }

    /**
     * Calls a method of org.alfresco.service.cmr.workflowWorkflowService,
     * putting the parameters
     * from the list into a map, and creating a transaction if none is given.<br/>
     * 
     * @param transaction
     *            a transaction object. May NOT be null.
     * @param methodName
     *            the name of the method to call, case-sensitive.
     * @param methodParameters
     *            a list containing the method parameters in the order of the
     *            function's signature.
     *            CANNOT BE NULL.
     * @return an object of the return type of the function that was called.
     */
    public Object workflowRequestWrapper(AlfrescoTransaction transaction, String methodName,
            List<Object> methodParameters) {
        Map<String, Object> parameterMaps = new HashMap<String, Object>();
        int i = 0;
        for (Object parameter : methodParameters) {
            parameterMaps.put("arg" + i, parameter);
            i++;
        }

        return workflowRequestCall(transaction, methodName, parameterMaps);
    }

    /**
     * Executes a workflow request on Alfresco by calling the webscript.
     * 
     * @param transaction
     *            Alfresco transaction (for login)
     * @param methodName
     *            Method to call (mapped to a method in WorkflowService).
     *            <b>Case-sensitive</b>.
     * @param methodParameters
     *            Parameters needed by the method (same types as declared in
     *            WorkflowService)
     * @return The deserialized Object or null if exception
     */
    private Object workflowRequestCall(AlfrescoTransaction transaction, String methodName,
            Map<String, Object> methodParameters) {
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("method", methodName);
        Set<String> keys = methodParameters.keySet();
        for (String key : keys) {
            Object parameterValue = methodParameters.get(key);
            if (parameterValue instanceof String) {
                parameters.put(key, (String) parameterValue);
            } else {
                String xmlParameter = xstream.toXML(parameterValue);
                parameters.put(key, xmlParameter);
            }
        }
        String sresult;

        if (isInStandaloneMode()) {
            return null;
        }

        try {
            sresult = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_WORKFLOW);
        } catch (ServletException e) {
            return null;
        }
        Object result = null;
        if (StringUtils.trimToNull(sresult) != null) {
            result = xstream.fromXML(sresult);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private List<String> workflowGetCurrentTasks(String instanceId, AlfrescoTransaction trans) {
        List<String> result = new Vector<String>();
        // get the paths from the instance id, which should exist
        List<WorkflowPath> paths = null;
        ArrayList<Object> paramList = new ArrayList<Object>();
        paramList.add(instanceId);
        paths = (List<WorkflowPath>) workflowRequestWrapper(trans, "getWorkflowPaths", paramList);
        if (paths == null) {
            if (logger.isErrorEnabled()) {
                logger.error(MsgId.INT_ERR_NULL_WKFLW_INSTANCE_PATHS + instanceId);
            }
            return result;
        }
        // we need to probe all active paths
        for (WorkflowPath path : paths) {
            if (path.active) {
                // get the tasks for the path
                List<WorkflowTask> tasks = null;
                ArrayList<Object> params = new ArrayList<Object>();
                params.add(path.id);
                tasks = (List<WorkflowTask>) workflowRequestWrapper(trans, "getTasksForWorkflowPath", params);
                if (tasks == null) {
                    return result;
                }
                // add the tasks to complete
                for (WorkflowTask task : tasks) {
                    if (task.state == WorkflowTaskState.IN_PROGRESS) {
                        result.add(task.id);
                    }
                }
            }
        }
        return result;
    }

    public WorkflowTaskDefinition workflowGetTaskDefinition(String processDefId, String task,
            AlfrescoTransaction trans) {

        // List<WorkflowTaskDefinition> taskDefs = workflowGetTaskDefinitions(processDefId, trans);
        // for (WorkflowTaskDefinition taskDef : taskDefs) {
        // if (StringUtils.equals(taskDef.id, task)) {
        // return taskDef;
        // }
        // }
        // return null;
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("processDefId", processDefId);
        parameters.put("task", "" + task);

        return (WorkflowTaskDefinition) workflowRequestCall(trans, "wfGetTaskDefinition", parameters);
    }

    // @SuppressWarnings("unchecked")
    // public List<WorkflowTaskDefinition> workflowGetTaskDefinitions(String processDefId,
    // AlfrescoTransaction trans) {
    // String methodName = "getTaskDefinitions";
    // List<Object> params = new ArrayList<Object>();
    // params.add(processDefId);
    // List<WorkflowTaskDefinition> workflowRequest = (List<WorkflowTaskDefinition>)
    // workflowRequestWrapper(
    // trans, methodName, params);
    // return workflowRequest;
    // }

    @SuppressWarnings("unchecked")
    public List<String> workflowGetWorkflowsForContent(String refStr, boolean onlyActive,
            AlfrescoTransaction trans) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("nodeId", refStr);
        parameters.put("onlyActive", "" + onlyActive);

        return (List<String>) workflowRequestCall(trans, "wfGetWorkflowsForContent", parameters);
    }

    public WorkflowDefinition workflowGetWorkflowById(String defId, AlfrescoTransaction trans) {
        String methodName = "getDefinitionById";
        List<Object> params = new ArrayList<Object>();
        params.add(defId);
        WorkflowDefinition workflowRequest = (WorkflowDefinition) workflowRequestWrapper(trans, methodName, params);
        return workflowRequest;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowFormNameByTaskId(String fullTaskId) {
        return mappingAgent.getWorkflowFormNameByTaskId(fullTaskId);
    }

    /**
     * Returns the Alfresco name for the given field from a specific workflow
     * form.
     * 
     * @param wkFormName
     * @param fieldName
     * @return
     */
    private String getWorkflowFieldAlfrescoName(String wkFormName, String fieldName) {
        return mappingAgent.getWorkflowFieldAlfrescoName(wkFormName, fieldName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String createPathInRepository(String userName, String folderPath) throws ServletException {

        // collect parameters
        Map<String, String> parameters = new TreeMap<String, String>();
        parameters.put("path", folderPath);
        // call the webscript
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        String resultId = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_MKDIR);

        return resultId;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String workflowExtractTaskNameFromFormName(String formName) {
        // e.g. "Evaluation_Demarrage" -> "Demarrage"
        return formName.substring(formName.indexOf("_") + 1);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String workflowExtractProcessNameFromFormName(String formName) {
        // e.g. "Evaluation_Demarrage" -> "Evaluation"
        return formName.substring(0, formName.indexOf("_"));
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String workflowExtractNamespacePrefix(String name) {
        int start = name.indexOf(BLUEXML_WORKFLOW_PREFIX);
        int end = name.indexOf(':');
        if ((start == -1) || (end == -1) || (end < start)) {
            return name;
        }
        String prefix = name.substring(start, end);
        return prefix;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean workflowIsBlueXMLDefinition(String name) {
        int start = name.indexOf(BLUEXML_WORKFLOW_PREFIX);
        int end = name.indexOf(':');
        if (start == -1 || end == -1 || (end < start)) {
            return false;
        }
        String prefix = name.substring(start, end);
        String processName = name.substring(end + 1);
        return StringUtils.endsWith(prefix, processName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String workflowExtractProcessNameFromDefName(String name) {
        return name.substring(name.indexOf(':') + 1);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowBlueXMLDefinitionName(String name) {
        // Used when reacting to a workflow transition action.
        // e.g. "Evaluation" --> "jbpm$wfbxEvaluation:Evaluation"
        return "jbpm$" + BLUEXML_WORKFLOW_PREFIX + name + ":" + name;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowBlueXMLTaskName(String formName) {
        // e.g. "Evaluation_Annotation" --> "wfbxEvaluation:Annotation"<br/>
        // NOTE: a duplicate of this function is also defined in MappingGenerator
        return BLUEXML_WORKFLOW_PREFIX + formName.replace('_', ':');
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowFormNameFromTask(String taskName) {
        // e.g. "wfbxEvaluation:Annotation" --> "Evaluation_Annotation"<br/>
        // e.g. "jbpm$wfbxEvaluation:Annotation" --> "Evaluation_Annotation"<br/>
        String searchString = BLUEXML_WORKFLOW_PREFIX;

        if (taskName.indexOf(searchString) != 0) {
            searchString = "jbpm$" + BLUEXML_WORKFLOW_PREFIX; // the demo webapp uses this format
            if (taskName.indexOf(searchString) != 0) {
                return null;
            }
        }

        String substr = taskName.substring(searchString.length());
        return substr.replace(':', '_');
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowNamespaceURI(String processName) {
        return MsgId.INT_NAMESPACE_BLUEXML_WORKFLOW + "/" + processName + "/1.0";
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWorkflowStartTaskFormName(String workflowDefName) {
        String prefix = workflowExtractNamespacePrefix(workflowDefName);

        // we got "wfbxwfTest" but we want "wfTest"
        prefix = prefix.substring(BLUEXML_WORKFLOW_PREFIX.length());

        return mappingAgent.getWorkflowStartTaskFormName(prefix);
    }

    @SuppressWarnings("unchecked")
    public List<String> workflowGetPooledTasks(String userName, AlfrescoTransaction trans) {

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userName", userName);

        return (List<String>) workflowRequestCall(trans, "wfGetPooledTasks", parameters);
    }

    public WorkflowTask workflowGetTaskById(String taskId, AlfrescoTransaction trans) {
        String methodName = "getTaskById";
        List<Object> methodParameters = new ArrayList<Object>();
        methodParameters.add(taskId);

        WorkflowTask workflowRequest = (WorkflowTask) workflowRequestWrapper(trans, methodName, methodParameters);
        return workflowRequest;
    }

    @SuppressWarnings("unchecked")
    public List<String> workflowGetPackageContents(String taskId, AlfrescoTransaction trans) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("taskId", taskId);

        return (List<String>) workflowRequestCall(trans, "wfGetPackageContents", parameters);

    }

    /**
     * Sets initial values for workflow fields from BlueXML properties (stored
     * in the repository) of
     * the workflow instance.
     * 
     * @param wkFormName
     *            the name of the workflow form to display
     * @param doc
     *            the XForms instance
     * @param instanceId
     *            the workflow instance Id (previously provided by Alfresco)
     * @return false if a lethal exception occurred. True if the normal end of
     *         the function was
     *         reached, which does not imply anything about the setting of
     *         initial values.
     */
    public boolean workflowPatchInstance(AlfrescoTransaction transaction, String wkFormName, Document doc,
            String instanceId) {
        if (logger.isDebugEnabled()) {
            logger.debug("Patching workflow instance with Id:'" + instanceId + "', form name: " + wkFormName);
        }
        QName qname;
        String namespaceURI = null; // to be set once
        Map<QName, Serializable> properties = null; // to be set once

        if (StringUtils.trimToNull(instanceId) == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("  No patching performed: the instanceId is null");
            }
            return true;
        }
        if (instanceId.equals("null")) {
            if (logger.isDebugEnabled()) {
                logger.debug("  No patching performed, invalid instanceId with string 'null'");
            }
            return true;
        }
        Element root = doc.getDocumentElement();
        Element formElt = DOMUtil.getChild(root, wkFormName);
        List<Element> allFields = DOMUtil.getAllChildren(formElt);

        // we need to fail silently so that the form is displayed even in the event of errors
        for (Element field : allFields) {
            String fieldUniqueName = field.getTagName();
            Serializable fieldValue = null;
            String localName = getWorkflowFieldAlfrescoName(wkFormName, fieldUniqueName);
            if (localName != null) {
                // build the QName
                if (namespaceURI == null) {
                    String processName = workflowExtractProcessNameFromFormName(wkFormName);
                    namespaceURI = getWorkflowNamespaceURI(processName);
                }
                qname = QName.createQName(namespaceURI, localName);

                // read the QName value from the collected properties of the workflow instance
                if (properties == null) {
                    properties = workflowCollectInstanceProperties(transaction, instanceId);
                    if (properties == null) {
                        return false; // there's no point in continuing without the properties
                    }
                }
                try {
                    // set the initial value
                    fieldValue = properties.get(qname);
                    if (fieldValue != null) {
                        field.setTextContent(fieldValue.toString());
                    }
                } catch (NullPointerException e) {
                    // we'll get this when displaying workflow forms while the webscript is down
                    return false;
                }
            }
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public void setStandaloneMode(boolean standaloneMode) {
        AlfrescoController.standaloneMode = standaloneMode;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isInStandaloneMode() {
        return standaloneMode;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public void setCssUrl(String cssUrl) {
        CssUrl = cssUrl;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getCssUrl() {
        return CssUrl;
    }

    /**
     * Gets the suffix appended to read only forms. <br/>
     * NOTE: In the BxDS version, that suffix is configured at generation time
     * and stored in the
     * mapping file. Here (in SIDE) we don't have (yet) any means to do the
     * same.
     * 
     * @return the suffix appended to names of read-write forms to produce the
     *         names of the
     *         read-only versions of the same forms.
     */
    public String getReadOnlyFormsSuffix() {
        return mappingAgent.getReadOnlyFormsSuffix();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isInDebugMode() {
        return mappingAgent.getDebugModeStatus();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isStartTaskForm(String wkFormName) { // PUBLIC-API
        return mappingAgent.isStartTaskForm(wkFormName);
    }

    /**
     * Gets the actual data type for a form. Added because read only forms are
     * distinguished from
     * R/W forms using a suffix. Hence the form name in itself cannot be used to
     * designate the
     * datatype (or "form id" to be more precise).
     * 
     * @param formName
     * @return the id of the read-write version of the form
     */
    public String getDataTypeFromFormName(String formName) {
        String underlyingDataType = formName;
        String readOnlyFormsSuffix = getReadOnlyFormsSuffix();
        if (StringUtils.endsWith(formName, readOnlyFormsSuffix)) {
            underlyingDataType = StringUtils.removeEnd(formName, readOnlyFormsSuffix);
        }
        return underlyingDataType;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean authenticate(String username, String password) { // PUBLIC-API
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, username);
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("username", username);
        parameters.put("password", password);
        transaction.setLogin(username);
        String result;
        try {
            result = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_AUTHENTICATE);
        } catch (ServletException e) {
            e.printStackTrace();
            return false;
        }
        return StringUtils.equalsIgnoreCase(result, "success");
    }

    public String getWebscriptHelp(AlfrescoTransaction transaction) {
        String result;
        Map<String, String> parameters = new HashMap<String, String>();
        try {
            result = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_HELP);
        } catch (ServletException e) {
            e.printStackTrace();
            return null;
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean performDynamicReload() { // PUBLIC-API
        try {
            mappingAgent.loadMappingXml(false);
        } catch (Exception e) {
            logger.error("Error while loading the dynamic reload", e);
            return false;
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getNodeType(String userName, String dataId) { // PUBLIC-API
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);
        transaction.setLogin(userName);

        QName qname = systemGetNodeType(transaction, patchDataId(dataId));
        if (qname == null) {
            return null;
        }
        return qname.getLocalName();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getNodeTypeFull(String userName, String dataId) { // PUBLIC-API
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);
        transaction.setLogin(userName);

        QName qname = systemGetNodeType(transaction, dataId);
        if (qname == null) {
            return null;
        }
        return qname.toString();
    }

    public String getNodeContentInfo(AlfrescoTransaction transaction, String nodeId) {
        if (StringUtils.trimToNull(nodeId) == null) {
            return "";
        }
        String request;
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("nodeId", nodeId);

        try {
            request = requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_NODE_INFO);
        } catch (ServletException e) {
            e.printStackTrace();
            return null;
        }

        if (StringUtils.trimToNull(request) == null) {
            return "";
        }

        String result;
        String[] infos = request.split(","); // <- see the webscript for the actual format
        String nodeName = infos[0]; // the node name
        long size = Long.parseLong(infos[1]); // the content's size
        if (size > 0) {
            String sizeUnit = "";
            Formatter formatter = new Formatter();
            String sizeBytes = formatter.format("%,d", size).toString();
            char multiplier = getFileSizeUnit(size);
            if (multiplier != 'b') {
                sizeUnit = " (" + getFileSizeShortReadable(size, multiplier) + ")";
            } else {
                //
            }
            result = MsgPool.getMsg(MsgId.MSG_UPLOAD_CONTENT_REPO_FORMAT, nodeName, sizeBytes, sizeUnit, nodeId);
        } else {
            result = MsgPool.getMsg(MsgId.MSG_UPLOAD_CONTENT_NO_CONTENT);
        }
        // result += " " + result;
        return result;
    }

    /**
     * Gets the "appropriate" unit for the given file size.
     * 
     * @param size
     * @return 'b' or 'k' or 'm' or 'g'
     */
    private static char getFileSizeUnit(long size) {
        char unit = 'b'; // bytes
        if (size > 1024 * 1024 * 1024) {
            unit = 'g';
        } else if (size > 1024 * 1024) {
            unit = 'm';
        } else if (size > 1024) {
            unit = 'k';
        }
        return unit;
    }

    /**
     * Returns a "short" version of the given file size using the appropriate
     * multiplier, and
     * formatted with a fixed number (see the format) of decimal places. <br/>
     * Examples: 1024 bytes => 1.00 KB, 7 614 525 bytes => 7.26 MB
     * 
     * @param transferred
     * @param unit
     * @return
     */
    private static String getFileSizeShortReadable(long transferred, char unit) {
        float res = transferred;
        String unitStr = "B";
        switch (unit) {
        case 'k':
        case 'K':
            res = (float) transferred / (float) (1024);
            unitStr = "KB";
            break;
        case 'm':
        case 'M':
            res = (float) transferred / (float) (1024 * 1024);
            unitStr = "MB";
            break;
        case 'g':
        case 'G':
            res = (float) transferred / (float) (1024 * 1024 * 1024);
            unitStr = "GB";
            break;
        }
        Formatter formatter = new Formatter();
        return formatter.format("%,.2f", res) + " " + unitStr;
    }

    //
    // REDIRECTION
    //

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public RedirectionBean getWorkflowRedirectionBean(String formName) {
        return targetTable.get(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean loadRedirectionTable(String filePath) {
        if (logger.isDebugEnabled()) {
            logger.debug("Reading redirection configuration file.");
        }

        String actualFilePath = ""; // the path where the stream is successfully open
        FileInputStream stream = null;
        if (StringUtils.trimToNull(filePath) != null) {
            try {
                actualFilePath = filePath;
                File file = new File(actualFilePath);
                stream = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace(); // continue anyway
            }
        }

        if (stream == null) {
            if (StringUtils.trimToNull(redirectXmlPath) != null) {
                try {
                    actualFilePath = redirectXmlPath;
                    File file = new File(actualFilePath);
                    stream = new FileInputStream(file);
                } catch (FileNotFoundException e) {
                    e.printStackTrace(); // continue anyway
                }
            }
            URL url = AlfrescoController.class.getResource("/redirect.xml");
            if (url == null) {
                if (logger.isErrorEnabled()) {
                    logger.error("Redirection file not found. Redirection will not be available.");
                }
                return false;
            }
            File file;
            try {
                URI fileURI = new URI(url.toString());
                file = new File(fileURI);
                actualFilePath = fileURI.getSchemeSpecificPart();
                stream = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return false;
            } catch (URISyntaxException e1) {
                e1.printStackTrace();
                return false;
            }

        } else {
            redirectXmlPath = filePath;
        }
        String formName;
        String url;
        boolean autoAdvance;
        boolean addParams;

        Document document = DOMUtil.getDocumentFromStream(stream);
        if (document == null) {
            return false;
        }

        HashMap<String, RedirectionBean> localTable = new HashMap<String, RedirectionBean>();

        // we won't check the tag name for the root element
        Element root = document.getDocumentElement();
        List<Element> entries = DOMUtil.getChildren(root, MsgId.INT_REDIRECTION_ENTRY.getText());
        // for each entry of the file, store the info
        for (Element entry : entries) {
            Element nameElt = DOMUtil.getChild(entry, MsgId.INT_REDIRECTION_NAME.getText());
            if (nameElt != null) {
                try {
                    formName = nameElt.getTextContent();
                    Element urlElt = DOMUtil.getChild(entry, MsgId.INT_REDIRECTION_URL.getText());
                    url = urlElt.getTextContent();
                    Element autoElt = DOMUtil.getChild(entry, MsgId.INT_REDIRECTION_AUTO_ADVANCE.getText());
                    autoAdvance = StringUtils.equals(autoElt.getTextContent(), "true");

                    Element addElt = DOMUtil.getChild(entry, MsgId.INT_REDIRECTION_ADD_PARAMS.getText());
                    addParams = StringUtils.equals(addElt.getTextContent(), "true");
                    RedirectionBean bean = new RedirectionBean(url, autoAdvance, addParams);

                    localTable.put(formName, bean);
                } catch (NullPointerException ne) {
                    logger.error("Caught a null pointer exception while loading the redirection file at '"
                            + actualFilePath + "'. The file is probably not correctly formatted.", ne);
                    return false;
                }
            } else {
                // // get rid of everything previously read
                // targetTable = new HashMap<String, RedirectionBean>();
                // return false;
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Redirection configuration file successfully read.");
        }

        targetTable = localTable;
        return true;
    }

    //
    // PUBLIC API
    //

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getCustomFormForDatatype(String dataType) {
        return mappingAgent.getCustomFormForDatatype(dataType);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getDefaultFormForDatatype(String dataType) {
        return mappingAgent.getDefaultFormForDatatype(dataType);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getUnderlyingTypeForForm(String formName) {
        return mappingAgent.getUnderlyingClassForForm(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getUnderlyingTypeForWorkflow(String formName) {
        return mappingAgent.getUnderlyingClassForWorkflow(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getUnderlyingDataFormForWorkflow(String formName) {
        return mappingAgent.getUnderlyingDataFormForWorkflow(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public WorkflowTaskInfoBean getWorkflowTaskInfoBean(String wkFormName) {
        return mappingAgent.getWorkflowTaskInfoBean(wkFormName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public WorkflowTaskInfoBean getWorkflowTaskInfoBeanByTaskId(String taskId) {
        return mappingAgent.getWorkflowTaskInfoBeanById(taskId);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> getAllCustomForms() {
        return mappingAgent.getAllCustomForms();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> getAllDefaultForms() {
        return mappingAgent.getAllDefaultForms();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> getAllSearchForms() {
        return mappingAgent.getAllSearchForms();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> getAllWorkflowForms() {
        return mappingAgent.getAllWorkflowForms();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Document getInstanceForm(String userName, String formName, String dataId, boolean formIsReadOnly)
            throws ServletException {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        GetInstanceFormBean bean = new GetInstanceFormBean(formName, patchDataId(dataId), formIsReadOnly, false,
                null);
        return getInstanceForm(transaction, bean);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Document getInstanceClass(String userName, String formName, String dataId, boolean formIsReadOnly,
            boolean applyUserFormats) throws ServletException {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        return getInstanceClass(transaction, formName, patchDataId(dataId), formIsReadOnly, applyUserFormats);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Document getInstanceWorkflow(String userName, String formName) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        return getInstanceWorkflow(transaction, formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Document readObjectFromRepository(String userName, String dataId) throws ServletException {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        return readObjectFromRepository(transaction, dataId);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isCustomForm(String formName) {
        return mappingAgent.isCustomForm(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isDefaultForm(String formName) {
        return mappingAgent.isDefaultForm(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isSearchForm(String formName) {
        return mappingAgent.isSearchForm(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public boolean isWorkflowForm(String formName) {
        return mappingAgent.isWorkflowForm(formName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> workflowGetCurrentTasks(String userName, String instanceId) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetCurrentTasks(instanceId, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> workflowGetPackageContents(String userName, String taskId) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetPackageContents(taskId, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> workflowGetPooledTasks(String userName, String managerUserName) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetPooledTasks(managerUserName, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public WorkflowTask workflowGetTaskById(String userName, String taskId) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetTaskById(taskId, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public WorkflowTaskDefinition workflowGetTaskDefinition(String userName, String processDefId, String task) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetTaskDefinition(processDefId, task, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public WorkflowDefinition workflowGetWorkflowById(String userName, String defId) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetWorkflowById(defId, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public List<String> workflowGetWorkflowsForContent(String userName, String nodeId, boolean onlyActive) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return workflowGetWorkflowsForContent(patchDataId(nodeId), onlyActive, trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getNodeContentInfo(String userName, String nodeId) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return getNodeContentInfo(trans, patchDataId(nodeId));
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String getWebscriptHelp(String userName) {
        AlfrescoTransaction trans = new AlfrescoTransaction(this, userName);

        return getWebscriptHelp(trans);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Set<String> systemGetAllAuthoritiesAsGroupsOrUsers(String userName, boolean asGroups) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        return systemGetAllAuthoritiesAsGroupsOrUsers(transaction, asGroups);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public Set<String> systemGetContainingGroups(String userName, String specificUserName) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        return systemGetContainingGroups(transaction, specificUserName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String systemGetNodeProperty(String userName, String propertyName, String nodeId) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        NodeRef node;

        int posProtocolStoreSep = nodeId.indexOf(':');
        int posStoreIdSep = nodeId.indexOf("://");
        if (posProtocolStoreSep == -1 && posStoreIdSep == -1) {
            node = new NodeRef(patchDataId(nodeId));
        } else {
            node = new NodeRef(nodeId);
        }
        QName propertyQName = QName.createQName(propertyName);
        return systemGetNodeProperty(transaction, node, propertyQName);
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String systemGetNodeRefForGroup(String userName, String groupName) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        NodeRef nodeRef = systemGetNodeRefForGroup(transaction, groupName);
        if (nodeRef == null) {
            return null;
        }
        return nodeRef.toString();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String systemGetNodeRefForUser(String userName, String specificUserName) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        NodeRef nodeRef = systemGetNodeRefForUser(transaction, specificUserName);
        if (nodeRef == null) {
            return null;
        }
        return nodeRef.toString();
    }

    /*
     * (non-Javadoc)
     * @see com.bluexml.xforms.controller.alfresco.AlfrescoControllerAPI
     */
    public String systemGetNodeType(String userName, String dataId) {
        AlfrescoTransaction transaction = new AlfrescoTransaction(this, userName);

        QName qname = systemGetNodeType(transaction, dataId);
        if (qname == null) {
            return null;
        }
        return qname.toString();
    }

    /**
     * Gets the id, label and qname of the ids (complete node ids, with protocol
     * and store) in the
     * comma-separated list
     * 
     * @param transaction
     * @param format
     *            the (one) pattern for formatting the labels
     * @param ids
     *            the comma-separated list of ids
     * @return
     * @throws ServletException
     */
    public String readObjectsInfo(AlfrescoTransaction transaction, String format, String ids)
            throws ServletException {
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("ids", ids);
        parameters.put("format", format);
        return requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_NODE_INFO);
    }

    /**
     * Gets the complete id, label and qname for the node (of the given content
     * type) whose
     * identifier property has a specific value. selection-capable form field
     * initialized using a
     * URL parameter. For this function to do its job correctly, there should be
     * an object of the
     * datatype whose identifier property has the initialization value. Unless
     * specified, the
     * parameters MUST NOT be <code>null</code>.
     * 
     * @param transaction
     * @param datatype
     *            the prefixed content type name to search.
     * @param identifier
     *            the local name of a property (in the datatype definition) used
     *            as an identifier.
     * @param format
     *            the pattern for formatting the label. <code>null</code>-able.
     * @param labelLength
     *            the maximum length allowed for the label. <code>null</code>
     *            -able.
     * @param value
     *            the initialization value, also the value (of the identifier
     *            property) to look for.
     * @return
     * @throws ServletException
     */
    public String resolveObjectInfo(AlfrescoTransaction transaction, String datatype, String identifier,
            String format, String labelLength, String value) throws ServletException {
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("datatype", datatype);
        parameters.put("identifier", identifier);
        parameters.put("format", StringUtils.trimToEmpty(format));
        parameters.put("id", value);
        String maxLength = StringUtils.trimToNull(labelLength) == null ? "0" : labelLength;
        parameters.put("labelLength", maxLength);
        return requestString(transaction, parameters, MsgId.INT_WEBSCRIPT_OPCODE_NODE_INFO);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_FAILURE_PAGE for a form of a
     * given type.
     * 
     * @param formName
     *            the id of the workflow form
     */
    public String getXtensionFailurePage(String formName) {
        return mappingAgent.getXtensionFailurePage(formName);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_SUCCESS_PAGE for a form of a
     * given type.
     * 
     * @param formName
     *            the id of the workflow form
     */
    public String getXtensionSuccessPage(String formName) {
        return mappingAgent.getXtensionSuccessPage(formName);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_NEXT_PAGE_CANCEL for a form of
     * a given type.
     * 
     * @param formName
     *            the id of the form
     * @param formTypeEnum
     */
    public String getXtensionNextPageCancel(String formName, FormTypeEnum formTypeEnum) {
        return mappingAgent.getXtensionNextPageCancel(formName, formTypeEnum);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_NEXT_PAGE_DELETE for a form of
     * a given type.
     * 
     * @param formName
     *            the id of the form
     * @param formTypeEnum
     */
    public String getXtensionNextPageDelete(String formName, FormTypeEnum formTypeEnum) {
        return mappingAgent.getXtensionNextPageDelete(formName, formTypeEnum);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_NEXT_PAGE_SUBMIT for a form of
     * a given type.
     * 
     * @param formName
     *            the id of the form
     * @param formTypeEnum
     */
    public String getXtensionNextPageSubmit(String formName, FormTypeEnum formTypeEnum) {
        return mappingAgent.getXtensionNextPageSubmit(formName, formTypeEnum);
    }

    /**
     * Gets the value of the MsgId.MODEL_XTENSION_SKIP_ADDITIONAL_INFO for a
     * form of a given type.
     * 
     * @param formName
     *            the id of the form
     * @param formTypeEnum
     */
    public boolean getXtensionSkipAdditionalInfo(String formName, FormTypeEnum formTypeEnum) {
        return mappingAgent.getXtensionSkipAdditionalInfo(formName, formTypeEnum);
    }

}