Turbine表单处理中的中文

来源:互联网 发布:网络骑士 异世帝王行 编辑:程序博客网 时间:2024/06/06 04:23

Turbineapache 项目中的server-side java技术,位于jakarta子项目,是基于servletweb应用框架.它提供了很多基础服务:访问控制,页面个性化,服务调度,表单确认,xml-rpc格式的web服务等等.可以做为开发面向服务架构应用的基础,因为turbine很容易开发其它服务,并在其服务管理框架下运行.其下一个版本为2.4,它明确使用亚瑟王神剑项目(该项目实现了IOC模式)来管理服务组件。

 

 

本文描述的场景是:turbine2.31+velocity-1.4+torque-3.1。要解决在该场景下提交编码模式为multipart/form-data的表单不能正确处理中文的问题,虽然application/x-www-form-urlencoded编码下可以正确处理中文.

 

 

纠正本问题需要提供:Maven,Ant两个工具以及Turbine源码.

 

 

首先,Turbine的入口servlet Turbine.java中的代码片段,doGet方法:

目的是看原始的web服务器请求对象(request)是如何被传递入Turbine中的,以及Turbine如何处理request对象.

    public final void doGet(HttpServletRequest req, HttpServletResponse res)

            throws IOException, ServletException

    {

        // set to true if the request is to be redirected by the page

        boolean requestRedirected = false;

 

 

        // Placeholder for the RunData object.

        RunData data = null;

        try

        {

            // Check to make sure that we started up properly.

            if (initFailure != null)

            {

                throw initFailure;

            }

 

 

            // Get general RunData here...

            // Perform turbine specific initialization below.

 

 

            data = rundataService.getRunData(req, res, getServletConfig());

 

 

            // If this is the first invocation, perform some

            // initialization.  Certain services need RunData to initialize

            // themselves.

            if (firstDoGet)

            {

                init(data);

            }

 

 

            // set the session timeout if specified in turbine's properties

            // file if this is a new session

            if (data.getSession().isNew())

            {

                int timeout = configuration.getInt(SESSION_TIMEOUT_KEY,

                                                   SESSION_TIMEOUT_DEFAULT);

 

 

                if (timeout != SESSION_TIMEOUT_DEFAULT)

                {

                    data.getSession().setMaxInactiveInterval(timeout);

                }

            }

 

 

            // Fill in the screen and action variables.

            data.setScreen(data.getParameters().getString(URIConstants.CGI_SCREEN_PARAM));

            data.setAction(data.getParameters().getString(URIConstants.CGI_ACTION_PARAM));

 

 

            // Special case for login and logout, this must happen before the

            // session validator is executed in order either to allow a user to

            // even login, or to ensure that the session validator gets to

            // mandate its page selection policy for non-logged in users

            // after the logout has taken place.

            if (data.hasAction())

            {

                String action = data.getAction();

                log.debug("action = " + action);

 

 

                if (action.equalsIgnoreCase(

                        configuration.getString(ACTION_LOGIN_KEY,

                                                ACTION_LOGIN_DEFAULT)))

                {

                    loginAction(data);

                }

                else if (action.equalsIgnoreCase(

                        configuration.getString(ACTION_LOGOUT_KEY,

                                                ACTION_LOGOUT_DEFAULT)))

                {

                   logoutAction(data);

                }

            }

 

 

            // This is where the validation of the Session information

            // is performed if the user has not logged in yet, then

            // the screen is set to be Login. This also handles the

            // case of not having a screen defined by also setting the

            // screen to Login. If you want people to go to another

            // screen other than Login, you need to change that within

            // TurbineResources.properties...screen.homepage; or, you

            // can specify your own SessionValidator action.

            ActionLoader.getInstance().exec(

                    data, configuration.getString(ACTION_SESSION_VALIDATOR_KEY,

                        ACTION_SESSION_VALIDATOR_DEFAULT));

 

 

            // Put the Access Control List into the RunData object, so

            // it is easily available to modules.  It is also placed

            // into the session for serialization.  Modules can null

            // out the ACL to force it to be rebuilt based on more

            // information.

            ActionLoader.getInstance().exec(

                    data, configuration.getString(ACTION_ACCESS_CONTROLLER_KEY,

                        ACTION_ACCESS_CONTROLLER_DEFAULT));

 

 

            // Start the execution phase. DefaultPage will execute the

            // appropriate action as well as get the Layout from the

            // Screen and then execute that. The Layout is then

            // responsible for executing the Navigation and Screen

            // modules.

            //

            // Note that by default, this cannot be overridden from

            // parameters passed in via post/query data. This is for

            // security purposes.  You should really never need more

            // than just the default page.  If you do, add logic to

            // DefaultPage to do what you want.

 

 

            String defaultPage = (templateService == null)

                    ? null :templateService.getDefaultPageName(data);

 

 

            if (defaultPage == null)

            {

                /*

                 * In this case none of the template services are running.

                 * The application may be using ECS for views, or a

                 * decendent of RawScreen is trying to produce output.

                 * If there is a 'page.default' property in the TR.props

                 * then use that, otherwise return DefaultPage which will

                 * handle ECS view scenerios and RawScreen scenerios. The

                 * app developer can still specify the 'page.default'

                 * if they wish but the DefaultPage should work in

                 * most cases.

                 */

                defaultPage = configuration.getString(PAGE_DEFAULT_KEY,

                        PAGE_DEFAULT_DEFAULT);

            }

 

 

            PageLoader.getInstance().exec(data, defaultPage);

 

 

            // If a module has set data.acl = null, remove acl from

            // the session.

            if (data.getACL() == null)

            {

                try

                {

                    data.getSession().removeAttribute(

                            AccessControlList.SESSION_KEY);

                }

                catch (IllegalStateException ignored)

                {

                }

            }

 

 

            // handle a redirect request

            requestRedirected = ((data.getRedirectURI() != null)

                                 && (data.getRedirectURI().length() > 0));

            if (requestRedirected)

            {

                if (data.getResponse().isCommitted())

                {

                    requestRedirected = false;

                    log.warn("redirect requested, response already committed: " +

                             data.getRedirectURI());

                }

                else

                {

                    data.getResponse().sendRedirect(data.getRedirectURI());

                }

            }

 

 

            if (!requestRedirected)

            {

                try

                {

                    if (data.isPageSet() == false && data.isOutSet() == false)

                    {

                        throw new Exception("Nothing to output");

                    }

 

 

                    // We are all done! if isPageSet() output that way

                    // otherwise, data.getOut() has already been written

                    // to the data.getOut().close() happens below in the

                    // finally.

                    if (data.isPageSet() && data.isOutSet() == false)

                    {

                        // Modules can override these.

                        data.getResponse().setLocale(data.getLocale());

                        data.getResponse().setContentType(

                                data.getContentType());

 

 

                        // Set the status code.

                        data.getResponse().setStatus(data.getStatusCode());

                        // Output the Page.

                        data.getPage().output(data.getOut());

                    }

                }

                catch (Exception e)

                {

                    // The output stream was probably closed by the client

                    // end of things ie: the client clicked the Stop

                    // button on the browser, so ignore any errors that

                    // result.

                    log.debug("Output stream closed? ", e);

                }

            }

        }

        catch (Exception e)

        {

            handleException(data, res, e);

        }

        catch (Throwable t)

        {

            handleException(data, res, t);

        }

        finally

        {

            // Return the used RunData to the factory for recycling.

            rundataService.putRunData(data);

        }

}

 

 

以及org.apache.turbine.services.rundata.TurbineRunDataService中的:

public RunData getRunData(HttpServletRequest req,

                              HttpServletResponse res,

                              ServletConfig config)

            throws TurbineException

    {

        return getRunData(DEFAULT_CONFIG, req, res, config);

    }

 

 

    /**

     * Gets a RunData instance from a specific configuration.

     *

     * @param key a configuration key.

     * @param req a servlet request.

     * @param res a servlet response.

     * @param config a servlet config.

     * @return a new or recycled RunData object.

     * @throws TurbineException if the operation fails.

     * @throws IllegalArgumentException if any of the parameters are null.

     */

    public RunData getRunData(String key,

                              HttpServletRequest req,

                              HttpServletResponse res,

                              ServletConfig config)

            throws TurbineException,

            IllegalArgumentException

    {

        // The RunData object caches all the information that is needed for

        // the execution lifetime of a single request. A RunData object

        // is created/recycled for each and every request and is passed

        // to each and every module. Since each thread has its own RunData

        // object, it is not necessary to perform syncronization for

        // the data within this object.

        if ((req == null)

            || (res == null)

            || (config == null))

        {

            throw new IllegalArgumentException("HttpServletRequest, "

                + "HttpServletResponse or ServletConfig was null.");

        }

 

 

        // Get the specified configuration.

        String[] cfg = (String[]) configurations.get(key);

        if (cfg == null)

        {

            throw new TurbineException("RunTime configuration '" + key + "' is undefined");

        }

 

 

        TurbineRunData data;

        try

        {

            data = (TurbineRunData) pool.getInstance(cfg[0]);

            data.setParameterParser((ParameterParser) pool.getInstance(cfg[1]));

            data.setCookieParser((CookieParser) pool.getInstance(cfg[2]));

        }

        catch (ClassCastException x)

        {

            throw new TurbineException("RunData configuration '" + key + "' is illegal", x);

        }

 

 

        // Set the request and response.

        data.setRequest(req);

        data.setResponse(res);

 

 

        // Set the servlet configuration.

        data.setServletConfig(config);

 

 

        // Set the ServerData.

        data.setServerData(new ServerData(req));

 

 

        return data;

    }

 

 

org.apache.turbine.services.rundata.DefaultTurbineRunData中的:

    /**

     * Sets the servlet request.

     *

     * @param req a request.

     */

    public void setRequest(HttpServletRequest req)

    {

        this.req = req;

    }

 

 

    /**

     * Sets the servlet response.

     *

     * @param res a response.

     */

    public void setResponse(HttpServletResponse res)

    {

        this.res = res;

    }

 

 

在该类中request对象被不加修改的传递到了ParameterParser:

    /**

     * Gets the parameters.

     *

     * @return a parameter parser.

     */

    public ParameterParser getParameters()

    {

        // Parse the parameters first, if not yet done.

        if ((this.parameters != null) &&

                (this.parameters.getRequest() != this.req))

        {

            this.parameters.setRequest(this.req);

        }

        return this.parameters;

    }

 

 

    /**

     * Gets the parameter parser without parsing the parameters.

     *

     * @return the parameter parser.

     */

    public ParameterParser getParameterParser()

    {

        return parameters;

    }

 

 

    /**

     * Sets the parameter parser.

     *

     * @param parser a parameter parser.

     */

    public void setParameterParser(ParameterParser parser)

    {

        parameters = parser;

    }

 

 

以上类中展示的代码都涉及到了Request对象,从这些代码中看出在Turbine,实际上并没有对Request对象做任何修改,这样就可以按照通常的方法,加入对Request对象的处理代码.最后, 这里是关键的地方:在类DefaultParameterParser中对Request对象的处理,也就是通过对该处的修改,问题得到了解决:

    /**

     * Sets the servlet request to be parser.  This requires a

     * valid HttpServletRequest object.  It will attempt to parse out

     * the GET/POST/PATH_INFO data and store the data into a Map.

     * There are convenience methods for retrieving the data as a

     * number of different datatypes.  The PATH_INFO data must be a

     * URLEncoded() string.

     * <p>

     * To add name/value pairs to this set of parameters, use the

     * <code>add()</code> methods.

     *

     * @param request An HttpServletRequest.

     */

    public void setRequest(HttpServletRequest request)

    {

        clear();

 

 

        uploadData = null;

 

 

        //String enc = request.getCharacterEncoding();

原来是:

        enc = request.getCharacterEncoding();

setCharacterEncoding(enc != null ? enc : "US-ASCII");

    修改后:

        if(enc==null)    enc=org.apache.turbine.Turbine.getConfiguration().getString(CharacterEncoding_Key,"US-ASCII");               

        setCharacterEncoding(enc);

        // String object re-use at its best.

        String tmp = null;

 

 

        tmp = request.getHeader("Content-type");

 

 

        if (uploadServiceIsAvailable

                && uploadService.getAutomatic()

                && tmp != null

                && tmp.startsWith("multipart/form-data"))

        {

            log.debug("Running the Turbine Upload Service");

            try

            {

                TurbineUpload.parseRequest(request, this);

            }

            catch (TurbineException e)

            {

                log.error("File upload failed", e);

            }

        }

 

 

        for (Enumeration names = request.getParameterNames();

             names.hasMoreElements();)

        {

            tmp = (String) names.nextElement();

            add(convert(tmp),

                    request.getParameterValues(tmp));

        }

 

 

        // Also cache any pathinfo variables that are passed around as

        // if they are query string data.

        try

        {

            StringTokenizer st =

                    new StringTokenizer(request.getPathInfo(), "/");

            boolean isNameTok = true;

            String pathPart = null;

            while (st.hasMoreTokens())

            {

                if (isNameTok)

                {

                    tmp = URLDecoder.decode(st.nextToken());

                    isNameTok = false;

                }

                else

                {

                    pathPart = URLDecoder.decode(st.nextToken());

                    if (tmp.length() > 0)

                    {

                        add(convert(tmp), pathPart);

                    }

                    isNameTok = true;

                }

            }

        }

        catch (Exception e)

        {

            // If anything goes wrong above, don't worry about it.

            // Chances are that the path info was wrong anyways and

            // things that depend on it being right will fail later

            // and should be caught later.

        }

 

 

        this.request = request;

 

 

        if (log.isDebugEnabled())

        {

            log.debug("Parameters found in the Request:");

            for (Iterator it = keySet().iterator(); it.hasNext();)

            {

                String key = (String) it.next();

                log.debug("Key: " + key + " -> " + getString(key));

            }

        }

    }

 

 

同时在配置文件TurbineResources.properties中加入一项:

CharacterEncoding=gb2312.可以根据需要修改该值.

 

 

完成以上修改后,Maven工具重新构建Turbine并得到了新的turbine.jar把它放入你的应用环境中,中文问题即得到正确显示.

   注意:以上代码片段来自Turbine源码.黑体字表示要关注的部分,代码中修改部分则给出了说明.

原创粉丝点击