com.gdo.servlet.RpcWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.gdo.servlet.RpcWrapper.java

Source

/**
 * Copyright GDO - 2005
 */
package com.gdo.servlet;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.util.Base64;

import com.gdo.helper.ConverterHelper;
import com.gdo.helper.StringHelper;
import com.gdo.project.model.AtomicActionStcl;
import com.gdo.project.model.ComposedActionStcl;
import com.gdo.project.model.ComposedActionStcl.Status;
import com.gdo.project.model.ServletStcl;
import com.gdo.project.util.CatalinaUtils;
import com.gdo.servlet.xml.XmlBuilder;
import com.gdo.sql.model.SQLContextStcl;
import com.gdo.stencils.Keywords;
import com.gdo.stencils.Result;
import com.gdo.stencils.StclContext;
import com.gdo.stencils._Stencil;
import com.gdo.stencils.cmd.CommandContext;
import com.gdo.stencils.cmd.CommandStatus;
import com.gdo.stencils.cmd.CommandStencil;
import com.gdo.stencils.faces.RenderContext;
import com.gdo.stencils.facet.FacetResult;
import com.gdo.stencils.facet.FacetType;
import com.gdo.stencils.iterator.StencilIterator;
import com.gdo.stencils.key.IKey;
import com.gdo.stencils.log.StencilLog;
import com.gdo.stencils.plug.PStcl;
import com.gdo.stencils.util.GlobalCounter;
import com.gdo.stencils.util.PathUtils;
import com.gdo.stencils.util.StencilUtils;
import com.gdo.util.XmlStringWriter;

/**
 * <p>
 * StudioGdo RPC interface wrapper.
 * </p>
 * 
 * <p>
 * &copy; 2004, 2008 StudioGdo/Guillaume Doumenc. All Rights Reserved. This
 * software is the proprietary information of StudioGdo &amp; Guillaume Doumenc.
 * Use is subject to license terms.
 * </p>
 */
public class RpcWrapper {

    public static boolean FACETS_SAME_FACET = false;

    //
    // RPC services
    //

    // call command on a stencil
    public static final String APPLY_SERVICE = "apply";

    // call command on a stencil
    public static final String CALL_SERVICE = "call";

    // disconnect to the server
    public static final String DISCONNECT_SERVICE = "disconnect";

    // call command on a stencil
    public static final String EMPTY_SERVICE = "empty";

    // get a facet of a stencil
    public static final String FACET_SERVICE = "facet";

    // get a facet of a stencil
    public static final String FACETS_SERVICE = "facets";

    // get formatted string
    public static final String FORMAT_SERVICE = "format";

    // get property value
    public static final String GET_SERVICE = "get";

    // launch a composed command on a stencil
    public static final String LAUNCH_SERVICE = "launch";

    // multi set property value
    public static final String MULTI_SET_SERVICE = "mset";

    // get property value without status
    public static final String PROP_SERVICE = "prop";

    // ping tomcat servlet
    public static final String PING_SERVICE = "ping";

    // classical post service
    public static final String POST_SERVICE = "post";

    // set property value
    public static final String SET_SERVICE = "set";

    // get stencils list in slot
    public static final String STENCILS_SERVICE = "stencils";

    // get attributes list in slot
    public static final String ATTRIBUTES_SERVICE = "attributes";

    //
    // RPC parameters
    //

    /**
     * Encoded path to the stencil on which entry will be applied.
     */
    public static final String ABSOLUTE_PATH_PARAM = "ap";

    /**
     * Encoded complement path to the stencil on which entry will be applied.
     */
    public static final String ABSOLUTE_COMPLEMENT_PATH_PARAM = "ap1";
    public static final String ABSOLUTE_COMPLEMENT_KEY_PARAM = "ak1";

    /**
     * Stencils attributes added to the STENCILS_SERVICE entry.
     */
    public static final String ATTRS_PARAM = "a";

    // accept entry even if no stencil
    public static final String ACCEPT_NO_STENCIL = "acceptNoStencil";

    // command name (if contains '.' then create instance from template name)
    public static final String CMD_PARAM = "c";

    // resulting data format (xml, json)
    public static final String DATA_FORMAT = "df";

    // encoding
    public static final String ENC_PARAM = "enc";

    // value expansion (none, 1=true, 0=false)
    public static final String EXP_PARAM = "exp";

    // facets
    public static final String FACETS_PARAM = "f";

    // expanded format
    public static final String FORMAT_PARAM = "format";

    // path where the command will be launched (defined for launch entry)
    public static final String LAUNCH_PATH_PARAM = "l";

    // locale
    public static final String LOCALE_PARAM = "locale";

    // modes
    public static final String MODES_PARAM = "m";

    // number (used for parameters, values, ..)
    public static final String NUMBER_PARAM = "n";
    public static final String PARAM_PREFIX = "param"; // parameters prefix

    // path to the stencil on which entry will be applied
    public static final String PATH_PARAM = "p";

    /*
     * complementary path and key
     * WARNING: p1 has predominence on k1
     * WARNING: ap1 has predominence on p1
     * WARNING: ak1 has predominence on ak1 
     */
    public static final String COMPLEMENT_PATH_PARAM = "p1";
    public static final String COMPLEMENT_KEY_PARAM = "k1";

    // release session before returning
    public static final String RELEASE_PARAM = "r";

    // save project before returning
    public static final String SAVE_PARAM = "s";

    // command target (same as stencil if not defined)
    public static final String TARGET_PARAM = "tg";
    public static final String TYPE_PARAM = "t"; // value type
    public static final String VALUE_PARAM = "v"; // value defined for set and
    public static final String TRANSACTION_ID_PARAM = "tid";
    // format entries

    // public static final String[] PARAMS_PARAM = new String[] { PARAM1_PARAM,
    // PARAM2_PARAM, PARAM3_PARAM, PARAM4_PARAM, PARAM5_PARAM };

    // property value type
    public static final String TYPE_TEXT = Keywords.TEXT; // return value as it
    public static final String TYPE_STRING = Keywords.STRING;
    public static final String TYPE_INT = Keywords.INT;
    public static final String TYPE_BOOLEAN = Keywords.BOOLEAN;
    public static final String TYPE_XINHA = Keywords.XINHA; // return value in

    private RpcWrapper(StclContext stclContext) {
        // singleton pattern
    }

    /**
     * @return <tt>true</tt> if the call is a connection call (used by servlet
     *         to accept entry).
     */
    public boolean isConnectCommand(StclContext stclContext, String entry, RpcArgs args) {
        if (CALL_SERVICE.equals(entry)) {
            logTrace(stclContext, "Checking connection command on new session");
            String cmd = args.getStringParameter(stclContext, CMD_PARAM);
            return ServletStcl.Command.CONNECT.equals(cmd);
        }
        return false;
    }

    /**
     * Main entry service.
     * 
     * @param stclContext
     *            the stencil context.
     * @param entry
     *            the RPC entry called.
     * @param args
     *            the RPC arguments
     */
    public void service(StclContext stclContext, String entry, RpcArgs args) {
        boolean disconnect = false;
        try {

            // gets arguments
            String trace = args.formatForTrace();
            if (CALL_SERVICE.equals(entry) || LAUNCH_SERVICE.equals(entry) || APPLY_SERVICE.equals(entry)) {
                String cmd = args.getStringParameter(stclContext, RpcWrapper.CMD_PARAM);
                logInfo(stclContext, "RPC %s:%s, cmd=%s", entry, trace, cmd);
            } else {
                logInfo(stclContext, "RPC %s:%s", entry, trace);
            }

            // checks entry not empty
            if (StringUtils.isBlank(entry)) {
                fault(stclContext, "", "Empty RPC entry", args);
            }

            // empty entry (used only to have post alone)
            if (EMPTY_SERVICE.equals(entry)) {
                String result = Result.success().jsonValue(Result.SUCCESS);
                StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), result,
                        args.getCharacterEncoding(stclContext));
                return;
            }

            // executes service
            if (STENCILS_SERVICE.equals(entry)) {
                stencils(stclContext, args);
            } else if (ATTRIBUTES_SERVICE.equals(entry)) {
                attributes(stclContext, args);
            } else if (PROP_SERVICE.equals(entry)) {
                prop(stclContext, args);
            } else if (GET_SERVICE.equals(entry)) {
                get(stclContext, args);
            } else if (FACET_SERVICE.equals(entry)) {
                facet(stclContext, args);
            } else if (FACETS_SERVICE.equals(entry)) {
                if (FACETS_SAME_FACET)
                    facet(stclContext, args);
                else
                    facets(stclContext, args);
            } else if (SET_SERVICE.equals(entry)) {
                set(stclContext, args);
            } else if (MULTI_SET_SERVICE.equals(entry)) {
                mset(stclContext, args);
            } else if (APPLY_SERVICE.equals(entry)) {
                apply(stclContext, args);
            } else if (CALL_SERVICE.equals(entry)) {
                call(stclContext, args);
            } else if (LAUNCH_SERVICE.equals(entry)) {
                launch(stclContext, args);
            } else if (FORMAT_SERVICE.equals(entry)) {
                format(stclContext, args);
            } else if (DISCONNECT_SERVICE.equals(entry)) {
                disconnect = true;
                StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), "",
                        args.getCharacterEncoding(stclContext));
            } else {
                // response.sendError(HttpServletResponse.SC_BAD_REQUEST, path);
                String msg = String.format("Unknown RPC entry %s", entry);
                fault(stclContext, entry, msg, args);
            }

            // releases session if required (close all connections before
            // deconnection)
            boolean release = args.getBooleanParameter(stclContext, RELEASE_PARAM, false);
            disconnect = release || disconnect;
            if (release || disconnect) {
                SQLContextStcl.closeAllConnections(stclContext);
                disconnect(stclContext);
            }
        } catch (Exception e) {
            fault(stclContext, entry, e, null);
        } finally {
            if (!disconnect)
                SQLContextStcl.closeAllConnections(stclContext);
        }
    }

    /**
     * Disconnect session.
     * 
     * @param stclContext
     *            the stencil context.
     * @param args
     *            the RPC arguments.
     */
    private void disconnect(StclContext stclContext) {
        stclContext.getSession().invalidate();
    }

    /**
     * Stencils list entry.
     * 
     * @param stclContext
     *            the stencil context.
     * @param args
     *            the RPC arguments.
     * @throws IOException
     */
    private void stencils(StclContext stclContext, RpcArgs args) throws IOException {

        // gets stencils iterator
        StencilIterator<StclContext, PStcl> iter = getStencilsFromArgPath(stclContext, args);
        if (iter.isNotValid() && !args.acceptNoStencil()) {
            String reason = iter.getStatus().getMessage();
            logWarn(stclContext, reason);
            fault(stclContext, STENCILS_SERVICE, reason, args);
            return;
        }

        // returns stencils found
        XmlBuilder builder = new XmlBuilder();
        String xml = builder.stencils(stclContext, args, iter);
        StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml, args.getCharacterEncoding(stclContext));
    }

    public void attributes(StclContext stclContext, RpcArgs args) {
        try {

            // gets stencils
            PStcl stcl = stclContext.getServletStcl();

            // returns no attribute if no path
            if (StringUtils.isBlank(args.getPath())) {
                fault(stclContext, STENCILS_SERVICE, "empty path", args);
                return;
            }

            // returns attributes found
            // !! TODO Path attribute seems wrong
            SortedMap<IKey, String[]> map = stcl.getAttributes(stclContext, args.getPath(),
                    args.getAttributePathes());
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            addStatus(writer, Result.success());
            writer.startElement("stencils");
            writer.writeAttribute("size", map.size());
            for (Entry<IKey, String[]> e : map.entrySet()) {
                writer.startElement("stencil");
                writer.writeAttribute("key", e.getKey().toString());
                writer.writeAttribute("attributes", e.getValue().length);
                int index = 0;
                for (String att : e.getValue()) {
                    writer.writeAttribute("attr" + index, att);
                    index++;
                }
                writer.endElement("stencil");
            }
            writer.endElement("stencils");
            writer.endElement("result");

            // traces and responds
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, STENCILS_SERVICE, e, null);
            return;
        }
    }

    private void prop(StclContext stclContext, RpcArgs args) {
        try {
            PStcl stcl = args.getStencilFromPath(stclContext);
            String value = getValue(stclContext, args);
            logTrace(stclContext, "gets property value '%s' from stencil %s", value, stcl);
            StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), value,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, PROP_SERVICE, e, null);
            return;
        }
    }

    private void get(StclContext stclContext, RpcArgs args) {
        try {
            PStcl stcl = args.getStencilFromPath(stclContext);
            String type = args.getStringParameter(stclContext, TYPE_PARAM);
            if (StringUtils.isBlank(type))
                type = TYPE_STRING;
            String value = getValue(stclContext, args);

            // returns value found
            XmlBuilder builder = new XmlBuilder();
            String xml = builder.get(stclContext, args, stcl, value, type, Result.success());
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, GET_SERVICE, e, null);
            return;
        }
    }

    private String getValue(StclContext stclContext, RpcArgs args) {

        // gets stencil
        String path = args.getPath();
        String parent_path = PathUtils.getPathName(path);
        String slot_path = PathUtils.getLastName(path);
        PStcl stcl = stclContext.getServletStcl();
        if (StringUtils.isNotBlank(parent_path))
            stcl = stcl.getStencil(stclContext, parent_path);

        // gets property type
        String type = args.getStringParameter(stclContext, TYPE_PARAM);
        if (StringUtils.isEmpty(type)) {
            type = TYPE_STRING;
        }

        // gets value found
        if (StencilUtils.isNull(stcl)) {
            if (args.acceptNoStencil()) {
                return "";
            }
            return StencilUtils.getNullReason(stcl);
        } else {
            if (TYPE_STRING.equals(type)) {
                return stcl.getString(stclContext, slot_path);
            } else if (TYPE_INT.equals(type)) {
                return Integer.toString(stcl.getInt(stclContext, slot_path));
            } else if (TYPE_BOOLEAN.equals(type)) {
                return Boolean.toString(stcl.getBoolean(stclContext, slot_path));
            }
            return String.format("unknown type %s", type);
        }
    }

    private void set(StclContext stclContext, RpcArgs args) {
        try {

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {
                if (!args.acceptNoStencil()) {
                    fault(stclContext, SET_SERVICE, StencilUtils.getNullReason(stcl), args);
                }
                return;
            }
            stcl = stcl.getContainer(stclContext);
            String p = PathUtils.getLastName(args.getPath());

            // gets value (if comming from www-form-encoded should decode
            // content)
            String value = args.getStringParameter(stclContext, VALUE_PARAM);
            value = getValueWithOrWithoutExpansion(stclContext, stcl, value, args);

            // gets type
            String type = args.getStringParameter(stclContext, TYPE_PARAM);
            if (StringUtils.isEmpty(type)) {
                type = TYPE_STRING;
            }

            // sets value
            if (TYPE_STRING.equals(type)) {
                stcl.setString(stclContext, p, value);
            } else if (TYPE_INT.equals(type)) {
                stcl.setInt(stclContext, p, Integer.parseInt(value));
            } else if (TYPE_BOOLEAN.equals(type)) {
                Boolean bool = ConverterHelper.parseBoolean(value);
                stcl.setBoolean(stclContext, p, bool);
            } else {
                String msg = String.format("unknown type %s", type);
                fault(stclContext, SET_SERVICE, msg, args);
                return;
            }

            // performs command after set/mset RPC calls
            stcl.afterRPCSet(stclContext);

            // writes result
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            args.writeAttributes(stclContext, stcl, true, writer);
            addStatus(writer, Result.success());
            writer.endElement("result");

            // traces and responds
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, SET_SERVICE, e, null);
            return;
        }
    }

    private void mset(StclContext stclContext, RpcArgs args) {
        try {
            Result result = Result.success();

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {
                if (!args.acceptNoStencil()) {
                    fault(stclContext, SET_SERVICE, StencilUtils.getNullReason(stcl), args);
                }
                return;
            }

            // do set service for each value
            Enumeration<String> e = (Enumeration<String>) stclContext.getRequest().getParameterNames();
            while (e.hasMoreElements()) {
                String param = e.nextElement();
                if (param.startsWith("param_")) {
                    int l = "param_".length();
                    String type = param.substring(l, l + 1);
                    String slot = param.substring(l + 2);

                    // string
                    if ("s".equals(type)) {

                        // gets encoded value
                        String value = args.getStringParameter(stclContext, param);
                        /*
                         * if
                         * (StringUtils.isNotEmpty(args.getCharacterEncoding(stclContext)))
                         * value = new
                         * String(value.getBytes(),args.getCharacterEncoding(stclContext));
                         * }
                         */
                        if (value == null) {
                            value = "";
                        }

                        // sets value
                        stcl.setString(stclContext, slot, value);
                    } else

                    // integer
                    if ("i".equals(type)) {
                        int value;
                        String str = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isEmpty(str)) {
                            value = 0;
                        } else {
                            value = Integer.parseInt(str);
                        }
                        stcl.setInt(stclContext, slot, value);
                    } else

                    // boolean
                    if ("b".equals(type)) {
                        boolean value;
                        String str = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isEmpty(str)) {
                            value = false;
                        } else {
                            value = ConverterHelper.parseBoolean(str);
                        }
                        stcl.setBoolean(stclContext, slot, value);
                    } else

                    // plug
                    if ("p".equals(type)) {
                        String path = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isNotBlank(path)) {
                            PStcl toBePlugged = stcl.getStencil(stclContext, path);
                            stcl.plug(stclContext, toBePlugged, slot);
                        } else {
                            stcl.clearSlot(stclContext, slot);
                        }
                    }
                }
            }

            // performs command after set/mset RPC calls
            stcl.afterRPCSet(stclContext);

            // writes result
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            args.writeAttributes(stclContext, stcl, true, writer);
            addStatus(writer, result);
            writer.endElement("result");

            // traces and responds
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, SET_SERVICE, e, null);
            return;
        }
    }

    /**
     * Executes a call entry.
     * 
     * @param stclContext
     *            the stencil context.
     * @param args
     *            the RPC arguments.
     */
    private void call(StclContext stclContext, RpcArgs args) {
        try {

            // gets stencils
            StencilIterator<StclContext, PStcl> iter = getStencilsFromArgPath(stclContext, args);
            if (iter.isNotValid()) {
                String reason = iter.getStatus().getMessage();
                String msg = logWarn(stclContext, "call service : cannot found stcl at path %s : %s",
                        args.getPath(), reason);
                fault(stclContext, CALL_SERVICE, msg, args);
                return;
            }

            // gets command parameter
            String cmd = args.getStringParameter(stclContext, CMD_PARAM);
            if (StringUtils.isBlank(cmd)) {
                String msg = logWarn(stclContext, "no command name defined (param %s)", CMD_PARAM);
                fault(stclContext, CALL_SERVICE, msg, args);
                return;
            }

            // executes command on every stencil
            CommandStatus<StclContext, PStcl> status = null;
            for (PStcl stcl : iter) {

                // gets command
                PStcl cmdStcl = stcl.getCommand(stclContext, cmd);
                if (StencilUtils.isNull(cmdStcl)) {
                    String msg = logWarn(stclContext, "cannot get command %s for stencil %s", cmd, stcl);
                    fault(stclContext, CALL_SERVICE, msg, args);
                    continue;
                }

                // executes command
                CommandContext<StclContext, PStcl> cmdContext = createCommandContext(stclContext, args, stcl);
                CommandStencil<StclContext, PStcl> actionStcl = (CommandStencil<StclContext, PStcl>) cmdStcl
                        .getReleasedStencil(stclContext);
                CommandStatus<StclContext, PStcl> res = actionStcl.execute(cmdContext, cmdStcl);
                if (status == null) {
                    status = res;
                } else {
                    status.addOther(res);
                }
            }

            // if the command forces redirection
            if (status != null && StringUtils.isNotEmpty(status.redirection)) {
                stclContext.getResponse().sendRedirect(status.redirection);
                return;
            }

            // writes result
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            // args.writeAttributes(stclContext, cmdStcl, true, writer);
            addStatus(writer, status);
            writer.endElement("result");

            // traces and response
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, CALL_SERVICE, e, null);
        }
    }

    private void launch(StclContext stclContext, RpcArgs args) {
        try {

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {
                String reason = StencilUtils.getNullReason(stcl);
                String msg = logWarn(stclContext, "launch service : cannot found stcl at path %s : %s",
                        args.getPath(), reason);
                fault(stclContext, LAUNCH_SERVICE, msg, args);
                return;
            }

            // gets launch path and key
            String launchPath = args.getStringParameter(stclContext, LAUNCH_PATH_PARAM);
            if (StringUtils.isBlank(launchPath)) {
                launchPath = "/Session/Launched";
            }

            // gets command parameter
            String cmd = args.getStringParameter(stclContext, CMD_PARAM);
            if (StringUtils.isEmpty(cmd)) {
                String msg = logWarn(stclContext, "no launch name defined (param %s)", CMD_PARAM);
                fault(stclContext, LAUNCH_SERVICE, msg, args);
                return;
            }

            // gets command and verifies it is composed
            PStcl cmdStcl = stcl.getCommand(stclContext, cmd);
            if (StencilUtils.isNull(cmdStcl)) {
                String msg = logWarn(stclContext, "cannot get command %s for stencil %s", cmd, stcl);
                fault(stclContext, LAUNCH_SERVICE, msg, args);
                return;
            }
            if (!(cmdStcl.getReleasedStencil(stclContext) instanceof ComposedActionStcl)) {
                String msg = logWarn(stclContext, "command %s in stencil %s is not a composed action", cmd, stcl);
                fault(stclContext, LAUNCH_SERVICE, msg, args);
                return;
            }

            // executes command and adds current launch path
            CommandContext<StclContext, PStcl> cmdContext = createCommandContext(stclContext, args, stcl);
            ComposedActionStcl actionStcl = (ComposedActionStcl) cmdStcl.getReleasedStencil(stclContext);
            CommandStatus<StclContext, PStcl> status = actionStcl.launch(cmdContext, launchPath, cmdStcl);

            // writes result
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            // args.writeAttributes(stclContext, stcl, true, writer);
            addStatus(writer, status);
            writer.endElement("result");

            // traces and response
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, LAUNCH_SERVICE, e, null);
        }
    }

    private void format(StclContext stclContext, RpcArgs args) {
        try {

            // get stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {
                String reason = StencilUtils.getNullReason(stcl);
                String msg = logWarn(stclContext, "format service : cannot found stcl at path %s : %s",
                        args.getPath(), reason);
                fault(stclContext, FORMAT_SERVICE, msg, args);
                return;
            }

            // get format value
            String format = args.getStringParameter(stclContext, VALUE_PARAM);
            if (StringUtils.isEmpty(format)) {
                String msg = logWarn(stclContext, "no format value (param %s)", VALUE_PARAM);
                fault(stclContext, FORMAT_SERVICE, msg, args);
                return;
            }

            // write result
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            args.writeAttributes(stclContext, stcl, true, writer);
            addStatus(writer, Result.success());
            writer.startElement("value");
            writer.writeCDATAElement("data", stcl.format(stclContext, format));
            writer.endElement("value");
            writer.endElement("result");

            // trace and response
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
        } catch (Exception e) {
            fault(stclContext, FORMAT_SERVICE, e, null);
            return;
        }
    }

    private void facet(StclContext stclContext, RpcArgs args) throws IOException {
        HttpServletResponse response = stclContext.getResponse();

        try {

            // gets facet type and mode
            String type = args.getStringParameter(stclContext, FACETS_PARAM);
            String mode = args.getStringParameter(stclContext, MODES_PARAM);
            if (StringUtils.isBlank(type)) {

                // the type is undefined
                String msg = String.format("no facet defined (param %s)", FACETS_PARAM);
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {

                // null stencil may be accepted
                if (args.acceptNoStencil()) {
                    StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), "",
                            args.getCharacterEncoding(stclContext));
                    return;
                }

                // stencil may not be null
                String reason = StencilUtils.getNullReason(stcl);
                String msg = String.format("facet service : cannot found stencil at path %s : %s", args.getPath(),
                        reason);
                response.sendError(HttpServletResponse.SC_NO_CONTENT, msg);
                return;
            }

            // searches facet from stencil
            RenderContext<StclContext, PStcl> renderCtxt = new RenderContext<StclContext, PStcl>(stclContext, stcl,
                    type, mode);
            FacetResult facetResult = stcl.getFacet(renderCtxt);
            if (facetResult.isNotSuccess()) {

                // error in facet
                response.sendError(HttpServletResponse.SC_NOT_FOUND, facetResult.getMessage());
                return;
            }

            // HTML facet
            if (FacetType.HTML.equals(type)) {
                StringWriter writer = new StringWriter();
                writer.write("<html>\n");
                writer.write(" <META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\n");
                writer.write(" <META HTTP-EQUIV=\"Expires\" CONTENT=\"-1\">\n");
                writer.write(" <META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\n");
                writer.write("<body>\n");
                IOUtils.copy(facetResult.getInputStream(), writer);
                facetResult.closeInputStream();
                writer.write("</body>\n</html>\n");
                String content = stcl.format(stclContext, writer.getBuffer().toString());
                StudioGdoServlet.writeHTMLResponse(response, content, args.getCharacterEncoding(stclContext));
                return;
            }

            // HTML 5 facet or JSON facet
            if (FacetType.HTML5.equals(type) || FacetType.DOM5.equals(type) || FacetType.TRANS.equals(type)
                    || FacetType.JSON.equals(type) || FacetType.JSKEL.equals(type) || FacetType.PYTHON.equals(type)
                    || FacetType.REST.equals(type)) {
                String mime = facetResult.getMimeType();
                InputStream in = facetResult.getInputStream();
                StudioGdoServlet.writeResponse(stclContext.getResponse(), HttpServletResponse.SC_OK, mime, in,
                        StclContext.getCharacterEncoding());
                return;
            }

            // file facet
            if (FacetType.FILE.equals(type)) {
                if (FacetType.E4X.equals(mode)) {
                    InputStream in = facetResult.getInputStream();
                    String enc = StclContext.getCharacterEncoding();
                    StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), in, enc);
                    facetResult.closeInputStream();
                    return;
                }
                CatalinaUtils.writeFileResponse(stclContext, facetResult);
                return;
            }

            // write result
            Reader reader = new InputStreamReader(facetResult.getInputStream());
            XmlStringWriter writer = new XmlStringWriter(args.getCharacterEncoding(stclContext));
            writer.startElement("result");
            args.writeAttributes(stclContext, stcl, false, writer);
            addStatus(writer, Result.success());

            // not escaped as XML may be used in data
            writer.writeCDATAElement("data", StringHelper.read(reader));
            writer.endElement("result");

            // trace and response
            String xml = writer.getString();
            logTrace(stclContext, xml);
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), xml,
                    args.getCharacterEncoding(stclContext));
            facetResult.closeInputStream();
        } catch (Exception e) {
            String msg = logError(stclContext, e.toString());
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
            return;
        }
    }

    private void facets(StclContext stclContext, RpcArgs args) throws IOException {
        HttpServletResponse response = stclContext.getResponse();
        try {

            // gets facet type and mode
            String type = args.getStringParameter(stclContext, FACETS_PARAM);
            String mode = args.getStringParameter(stclContext, MODES_PARAM);
            if (StringUtils.isBlank(type)) {

                // the type is undefined
                String msg = String.format("no facet defined (param %s)", FACETS_PARAM);
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }

            // HTML 5 facet or JSON facet
            if (FacetType.HTML5.equals(type) || FacetType.DOM5.equals(type) || FacetType.JSON.equals(type)
                    || FacetType.JSKEL.equals(type) || FacetType.PYTHON.equals(type)) {
                String mime = null;
                StringWriter str = new StringWriter();

                if (FacetType.JSON.equals(type) || FacetType.JSKEL.equals(type))
                    str.write("[");

                for (PStcl stcl : args.getStencilsFromPath(stclContext)) {

                    // on first stencil, mime variable is nor defined
                    if (mime != null && (FacetType.JSON.equals(type) || FacetType.JSKEL.equals(type)))
                        str.write(",");

                    // searches facet from stencil
                    RenderContext<StclContext, PStcl> renderCtxt = new RenderContext<StclContext, PStcl>(
                            stclContext, stcl, type, mode);
                    FacetResult facetResult = stcl.getFacet(renderCtxt);
                    if (facetResult.isNotSuccess()) {

                        // error in facet
                        response.sendError(HttpServletResponse.SC_NOT_FOUND, facetResult.getMessage());
                        return;
                    }

                    InputStream in = facetResult.getInputStream();
                    IOUtils.copy(in, str);
                    facetResult.closeInputStream();

                    if (mime == null)
                        mime = facetResult.getMimeType();
                }

                if (FacetType.JSON.equals(type) || FacetType.JSKEL.equals(type))
                    str.write("]");

                InputStream in = new ByteArrayInputStream(
                        str.toString().getBytes(StclContext.getCharacterEncoding()));
                StudioGdoServlet.writeResponse(stclContext.getResponse(), HttpServletResponse.SC_OK, mime, in,
                        StclContext.getCharacterEncoding());
                return;
            }

            // the mode is undefined
            String msg = String.format("cannot call facets on mode %s", mode);
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
            return;
        } catch (Exception e) {
            String msg = logError(stclContext, e.toString());
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
            return;
        }
    }

    // --------------------------------------------------------------------------
    //
    // HTML5 entries.
    //
    // --------------------------------------------------------------------------

    private void apply(StclContext stclContext, RpcArgs args) {
        try {

            String enc = args.getCharacterEncoding(stclContext);

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {

                // null stencil may be accepted
                if (args.acceptNoStencil()) {
                    StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), "", enc);
                    return;
                }

                // return error
                HttpServletResponse response = stclContext.getResponse();
                String msg = logInfo(stclContext, "apply : cannot found stencil at path %s : %s", args.getPath(),
                        StencilUtils.getNullReason(stcl));
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }

            // gets command parameter
            String cmd = args.getStringParameter(stclContext, CMD_PARAM);
            if (StringUtils.isEmpty(cmd)) {
                HttpServletResponse response = stclContext.getResponse();
                String msg = logWarn(stclContext, "apply : no command name defined (param %s)", CMD_PARAM);
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }

            // gets command and verifies it exist
            PStcl cmdStcl = stcl.getCommand(stclContext, cmd);
            if (StencilUtils.isNull(cmdStcl)) {
                HttpServletResponse response = stclContext.getResponse();
                String msg = logWarn(stclContext, "apply : cannot get command %s for stencil %s", cmd, stcl);
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }

            // executes atomic action
            if (cmdStcl.getReleasedStencil(stclContext) instanceof AtomicActionStcl) {

                // calls execution
                CommandContext<StclContext, PStcl> cmdContext = createCommandContext(stclContext, args, stcl);
                CommandStencil<StclContext, PStcl> actionStcl = (CommandStencil<StclContext, PStcl>) cmdStcl
                        .getReleasedStencil(stclContext);
                CommandStatus<StclContext, PStcl> result = actionStcl.execute(cmdContext, cmdStcl);

                // returns JSON result
                StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), result.jsonValue(result.getStatus()),
                        enc);
            } else

            // executes composed action
            if (cmdStcl.getReleasedStencil(stclContext) instanceof ComposedActionStcl) {

                // calls execution
                String launchPath = PathUtils.createPath("/Session/Launch", GlobalCounter.uniqueInt());
                CommandContext<StclContext, PStcl> cmdContext = createCommandContext(stclContext, args, stcl);
                ComposedActionStcl actionStcl = (ComposedActionStcl) cmdStcl.getReleasedStencil(stclContext);
                CommandStatus<StclContext, PStcl> result = actionStcl.launch(cmdContext, launchPath, cmdStcl);

                // encodes launched path
                Base64 base = new Base64();
                String launched = result.getInfo(CommandStatus.SUCCESS, ComposedActionStcl.class.getName(),
                        Status.LAUNCH_PATH);
                String encoded = new String(base.encode(launched.getBytes()));
                result.setInfo(CommandStatus.SUCCESS, ComposedActionStcl.class.getName(), Status.LAUNCH_PATH,
                        encoded);

                // returns JSON result
                StudioGdoServlet.writeHTMLResponse(stclContext.getResponse(), result.jsonValue(result.getStatus()),
                        enc);
            }

            // there is no other case
            else {
                HttpServletResponse response = stclContext.getResponse();
                String msg = logWarn(stclContext, "command %s in stencil %s is not an action", cmd, stcl);
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                return;
            }
        } catch (Exception e) {
            fault(stclContext, LAUNCH_SERVICE, e, null);
        }
    }

    public void doPost(StclContext stclContext, RpcArgs args) {
        try {

            // gets stencil
            PStcl stcl = args.getStencilFromPath(stclContext);
            if (StencilUtils.isNull(stcl)) {
                if (!args.acceptNoStencil()) {
                    fault(stclContext, SET_SERVICE, StencilUtils.getNullReason(stcl), args);
                }
                return;
            }

            // do set service for each value
            Enumeration<String> e = (Enumeration<String>) stclContext.getRequest().getParameterNames();
            while (e.hasMoreElements()) {
                String param = e.nextElement();
                if (StringUtils.isNotBlank(param) && param.length() > 2) {
                    String type = param.substring(0, 1);
                    String slot = param.substring(2);

                    // sets string value
                    if ("s".equals(type)) {
                        String value = args.getStringParameter(stclContext, param);
                        if (StringUtils.isEmpty(value)) {
                            stcl.setString(stclContext, slot, "");
                        } else {
                            stcl.setString(stclContext, slot, value);
                        }
                        continue;
                    }

                    // sets integer value
                    if ("i".equals(type)) {
                        String str = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isBlank(str)) {
                            stcl.setInt(stclContext, slot, 0);
                        } else {
                            stcl.setInt(stclContext, slot, Integer.parseInt(str));
                        }
                        continue;
                    }

                    // sets boolean value
                    if ("b".equals(type)) {
                        String str = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isBlank(str)) {
                            stcl.setBoolean(stclContext, slot, false);
                        } else {
                            stcl.setBoolean(stclContext, slot, ConverterHelper.parseBoolean(str));
                        }
                        continue;
                    }

                    // plugs value (value is path)
                    if ("p".equals(type)) {
                        String path = stclContext.getRequest().getParameter(param);
                        if (StringUtils.isNotBlank(path)) {
                            PStcl toBePlugged = stcl.getStencil(stclContext, path);
                            stcl.plug(stclContext, toBePlugged, slot);
                        } else {
                            stcl.clearSlot(stclContext, slot);
                        }
                    }
                }
            }

            // performs command after set/mset RPC calls
            stcl.afterRPCSet(stclContext);

        } catch (Exception e) {
            logError(stclContext, e.toString());
            fault(stclContext, FACET_SERVICE, e, null);
            return;
        }
    }

    /**
     * Returns a fault status as return.
     * 
     * @param stclContext
     *            the stencil context.
     * @param entry
     *            the servet entry called.
     * @param msg
     *            the displayed message.
     * @param args
     *            the RPC call arguments.
     */
    private void fault(StclContext stclContext, String entry, String msg, RpcArgs args) {
        try {
            String enc = (args != null) ? args.getCharacterEncoding(stclContext)
                    : StclContext.getCharacterEncoding();
            XmlStringWriter writer = new XmlStringWriter(enc);
            writer.startElement("fault");
            writer.writeAttribute("entry", entry);
            writer.writeAttribute("path", stclContext.getRequest().getParameter(PATH_PARAM));
            writer.writeCDATAElement("msg", msg);
            writer.endElement("fault");
            String content = writer.getString();
            StudioGdoServlet.writeXMLResponse(stclContext.getResponse(), content, enc);
            writer.close();
            logWarn(stclContext, content);
        } catch (IOException e) {
            logError(stclContext, "Cannot write %s message fault", msg);
        }
    }

    /**
     * Returns the stack trace of exception in fault.
     * 
     * @param stclContext
     *            the stencil context.
     * @param entry
     *            the RPC entry.
     * @param e
     *            the exception throwed.
     */
    private void fault(StclContext stclContext, String entry, Exception e, RpcArgs args) {
        String content = "";
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        content += sw.getBuffer().toString();
        fault(stclContext, entry, content, args);
    }

    private String getValueWithOrWithoutExpansion(StclContext stclContext, PStcl stcl, String value, RpcArgs args) {
        String exp = args.getStringParameter(stclContext, EXP_PARAM);
        if ("1".equals(exp) || "true".equals(exp)) {
            return stcl.format(stclContext, value);
        }
        return value;
    }

    /**
     * Creates the command context with associated parameters.
     * 
     * @param stclContext
     *            the stencil context.
     * @param args
     *            RPC arguments
     * @param stcl
     *            the stencil on which the command is called.
     * @return the command context.
     */
    private CommandContext<StclContext, PStcl> createCommandContext(StclContext stclContext, RpcArgs args,
            PStcl stcl) {

        // creates context
        PStcl targetStcl = stcl;
        String target = args.getStringParameter(stclContext, TARGET_PARAM);
        if (!StringUtils.isEmpty(target)) {
            targetStcl = stcl.getStencil(stclContext, target);
        }
        CommandContext<StclContext, PStcl> context = new CommandContext<StclContext, PStcl>(stclContext,
                targetStcl);

        // sets parameters
        Map<String, String[]> params = args.getParams(stclContext);
        Iterator<String> iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String name = iter.next();
            if (name.startsWith(PARAM_PREFIX)) {
                String[] param = params.get(name);
                if (param != null) {
                    context.setRedefinedParameter(name, param[0]);
                }
            }
        }

        return context;
    }

    /*
     * private String getXinhaFromMode(StclContext stclContext, PStcl stcl, String
     * path, String mode) throws UnsupportedEncodingException { String file =
     * mode; if (StringUtils.isEmpty(file)) file = "xinha/simple.html";
     * InputStream in = ClassHelper.getResourceAsStream(file,
     * stclContext.getLocale()); if (in == null) return ""; String out =
     * StringHelper.read(new InputStreamReader(in)); String content =
     * getStringFromMode(stclContext, stcl, path, "false", null);
     * HttpServletRequest request = stclContext.getRequest(); String baseHref =
     * request.getRequestURL().substring(0, request.getRequestURL().length() -
     * request.getRequestURI().length()); out = String.format(out, content,
     * stclContext.getLocale().getLanguage(), baseHref, stcl.getId(), path);
     * return stcl.format(stclContext, out); }
     */

    /**
     * Gets the unique servlet wrapper.
     * 
     * @param stclContext
     *            the stencil context
     * @return the unique servlet wrapper.
     */
    public static RpcWrapper getInstance(StclContext stclContext) {
        // ServletContext servContext =
        // stclContext.getHttpSession().getServletContext();
        // RpcWrapper wrapper = (RpcWrapper)
        // servContext.getAttribute(RpcWrapper.class.getName());
        // if (wrapper == null) {
        // wrapper = new RpcWrapper(stclContext);
        // servContext.setAttribute(RpcWrapper.class.getName(), wrapper);
        // }
        // return wrapper;
        return new RpcWrapper(stclContext);
    }

    /**
     * Returns a stencil iterator from the request argument path.
     * 
     * @param stclContext
     *            the stencil context.
     * @param args
     *            the request arguments.
     * @return a stencil iterator.
     */
    private StencilIterator<StclContext, PStcl> getStencilsFromArgPath(StclContext stclContext, RpcArgs args) {
        PStcl stcl = stclContext.getServletStcl();
        if (StringUtils.isBlank(args.getPath())) {
            return StencilUtils.<StclContext, PStcl>iterator(stclContext, stcl, stcl.getContainingSlot());
        }
        return stcl.getStencils(stclContext, args.getPath());
    }

    /**
     * Adds status info to XML answer.
     * 
     * @param writer
     *            the XML answer writer.
     * @param status
     *            the status to add.
     */
    private void addStatus(XmlStringWriter writer, Result status) throws IOException {

        // if status null (on iterator or stencil)
        if (status == null) {
            writer.startElement("status");
            writer.writeAttribute("level", Byte.toString(CommandStatus.SUCCESS));
            writer.endElement("status");
            return;
        }

        // writes status
        writer.startElement("status");
        writer.writeAttribute("level", Byte.toString(status.getStatus()));
        for (CommandStatus.ResultInfo comp : status.getInfos(CommandStatus.SUCCESS)) {
            if (comp != null) {
                writer.startElement("ok");
                writer.writeAttribute("cmdName", comp.getPrefix());
                writer.writeAttribute("index", comp.getIndex());
                if (comp.getValue() != null) {
                    writer.writeCDATA(comp.getValue().toString());
                }
                writer.endElement("ok");
            }
        }
        for (CommandStatus.ResultInfo comp : status.getInfos(CommandStatus.WARNING)) {
            if (comp != null) {
                writer.startElement("warn");
                writer.writeAttribute("cmdName", comp.getPrefix());
                writer.writeAttribute("index", comp.getIndex());
                if (comp.getValue() != null) {
                    writer.writeCDATA(comp.getValue().toString());
                }
                writer.endElement("warn");
            }
        }
        for (CommandStatus.ResultInfo comp : status.getInfos(CommandStatus.ERROR)) {
            if (comp != null) {
                writer.startElement("error");
                writer.writeAttribute("cmdName", comp.getPrefix());
                writer.writeAttribute("index", comp.getIndex());
                if (comp.getValue() != null) {
                    writer.writeCDATA(comp.getValue().toString());
                }
                writer.endElement("error");
            }
        }
        writer.endElement("status");
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    //
    // LOG PART
    //

    public static StencilLog getLog() {
        return _Stencil._LOG;
    }

    public static String logTrace(StclContext stclContext, String format, Object... params) {
        return getLog().logTrace(stclContext, format, params);
    }

    public static String logInfo(StclContext stclContext, String format, Object... params) {
        return getLog().logInfo(stclContext, format, params);
    }

    public static String logWarn(StclContext stclContext, String format, Object... params) {
        return getLog().logWarn(stclContext, format, params);
    }

    public static String logError(StclContext stclContext, String format, Object... params) {
        return getLog().logError(stclContext, format, params);
    }

}