Package net.paoding.rose.web.impl.thread

Source Code of net.paoding.rose.web.impl.thread.Rose

/*
* Copyright 2007-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.paoding.rose.web.impl.thread;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.paoding.rose.web.Dispatcher;
import net.paoding.rose.web.Invocation;
import net.paoding.rose.web.InvocationUtils;
import net.paoding.rose.web.RequestPath;
import net.paoding.rose.web.annotation.ReqMethod;
import net.paoding.rose.web.impl.mapping.EngineGroup;
import net.paoding.rose.web.impl.mapping.MappingNode;
import net.paoding.rose.web.impl.mapping.MatchResult;
import net.paoding.rose.web.impl.module.Module;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

/**
*
* @author 王志亮 [qieqie.wang@gmail.com]
*
*/
public class Rose implements EngineChain {

    protected static final Log logger = LogFactory.getLog(Rose.class);

    private final List<Module> modules;

    private final MappingNode mappingTree;

    private final RequestPath path;

    private final HttpServletRequest originalHttpRequest;

    private final HttpServletResponse originalHttpResponse;

    private boolean started;

    private List<LinkedEngine> engines = new ArrayList<LinkedEngine>(6);

    private List<MatchResult> matchResults;

    private InvocationBean inv;

    private int curIndexOfChain;

    private final LinkedList<AfterCompletion> afterCompletions = new LinkedList<AfterCompletion>();

    public Rose(List<Module> modules, MappingNode mappingTree, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse, RequestPath requestPath) {
        this.mappingTree = mappingTree;
        this.modules = modules;
        this.originalHttpRequest = httpRequest;
        this.originalHttpResponse = httpResponse;
        this.path = requestPath;
    }

    public MappingNode getMappingTree() {
        return mappingTree;
    }

    public InvocationBean getInvocation() {
        return inv;
    }

    public List<Module> getModules() {
        return modules;
    }

    public List<LinkedEngine> getEngines() {
        return engines;
    }

    /**
     * 启动rose逻辑,对请求进行匹配判断,如果匹配未能成功返回false; <br>
     * 对匹配成功的启动相关的架构处理逻辑直至整个请求的完成
     *
     * @return
     * @throws Throwable
     */
    public boolean start() throws Throwable {
        if (this.started) {
            throw new IllegalStateException("don't start again");
        }
        this.started = true;
        return innerStart();
    }

    public List<MatchResult> getMatchResults() {
        return matchResults;
    }

    /**
     * @throws IndexOutOfBoundsException
     */
    @Override
    public Object doNext() throws Throwable {
        return engines.get(--curIndexOfChain).execute(this);
    }

    private boolean innerStart() throws Throwable {
        final boolean debugEnabled = logger.isDebugEnabled();
        final List<MatchResult> matchResults = mappingTree.match(this.path);
        if (matchResults == null) {
            // not rose uri
            if (debugEnabled) {
                logger.debug("not rose uri: '" + this.path.getUri() + "'");
            }
            return false;
        }

        final MatchResult lastMatched = matchResults.get(matchResults.size() - 1);
        final EngineGroup leafEngineGroup = lastMatched.getMappingNode().getLeafEngines();
        if (leafEngineGroup.size() == 0) {
            // not rose uri
            if (debugEnabled) {
                logger.debug("not rose uri, not exits leaf engines for it: '" + this.path.getUri()
                        + "'");
            }
            return false;

        }
        final LinkedEngine leafEngine = select(leafEngineGroup.getEngines(path.getMethod()));
        if (leafEngine == null) {
            // 405 Method Not Allowed
            /*
             * The method specified in the Request-Line is not allowed for the
             * resource identified by the Request-URI. The response MUST include an
             * Allow header containing a list of valid methods for the requested
             * resource.
             */
            StringBuilder allow = new StringBuilder();
            final String gap = ", ";

            for (ReqMethod method : leafEngineGroup.getAllowedMethods()) {
                allow.append(method.toString()).append(gap);
            }
            if (allow.length() > 0) {
                allow.setLength(allow.length() - gap.length());
            }
            originalHttpResponse.addHeader("Allow", allow.toString());
            originalHttpResponse.sendError(405, this.path.getUri());

            // true: don't forward to next filter or servlet
            return true;

        }

        if (debugEnabled) {
            ActionEngine actionEngine = (ActionEngine) leafEngine.getTarget();
            logger.debug("mapped '" + path.getUri() + "' to "
                    + actionEngine.getControllerClass().getName() + "#"
                    + actionEngine.getMethod().getName());
        }

        // bind engines
        LinkedEngine tempEngine = leafEngine;
        MappingNode moduleNode = null;
        MappingNode controllerNode = null;
        while (tempEngine != null) {
            engines.add(tempEngine);
            Class<? extends Engine> target = tempEngine.getTarget().getClass();
            if (target == ModuleEngine.class) {
                moduleNode = tempEngine.getNode();
            } else if (target == ControllerEngine.class) {
                controllerNode = tempEngine.getNode();
            }
            tempEngine = tempEngine.getParent();
        }
        // set module/controller/action path to RequsetPath object
        StringBuilder sb = new StringBuilder(path.getUri().length());
        MatchResult moduleMatchResult = null;
        MatchResult controllerMatchResult = null;
        for (int i = 0; i < matchResults.size(); i++) {
            MatchResult matchResult = matchResults.get(i);
            sb.append(matchResult.getValue());
            if (matchResult.getMappingNode() == moduleNode) {
                moduleMatchResult = matchResult;
                path.setModulePath(sb.toString()); // modulePath
                Assert.notNull(engines.get(2));
                sb.setLength(0);
            }
            if (matchResult.getMappingNode() == controllerNode) {
                controllerMatchResult = matchResult;
                path.setControllerPath(sb.toString()); // controllerPath
                Assert.notNull(engines.get(1));
                sb.setLength(0);
            }
        }
        path.setActionPath(sb.toString()); // actionPath
        Assert.notNull(moduleMatchResult);
        Assert.notNull(controllerMatchResult);
        this.matchResults = matchResults;

        this.curIndexOfChain = engines.size();

        Map<String, String> uriParameters = null;
        for (int i = matchResults.size() - 1; i >= 0; i--) {
            MatchResult matchResult = matchResults.get(i);
            String name = matchResult.getParameterName();
            if (name != null) {
                if (uriParameters == null) {
                    uriParameters = new HashMap<String, String>(matchResults.size() << 1);
                }
                uriParameters.put(name, matchResult.getValue());
            }
        }

        HttpServletRequest httpRequest = originalHttpRequest;
        if (uriParameters != null && uriParameters.size() > 0) {
            httpRequest = new ParameteredUriRequest(originalHttpRequest, uriParameters);
        }

        // originalThreadRequest可能为null,特别是在portal框架下
        HttpServletRequest originalThreadRequest = InvocationUtils.getCurrentThreadRequest();
        //
        Invocation preInvocation = null;
        if (path.getDispatcher() != Dispatcher.REQUEST) {
            preInvocation = InvocationUtils.getInvocation(originalHttpRequest);
        }
        // invocation 对象 代表一次Rose调用
        InvocationBean inv = new InvocationBean(httpRequest, originalHttpResponse, path);
        inv.setRose(this);
        inv.setPreInvocation(preInvocation);
        //
        InvocationUtils.bindRequestToCurrentThread(httpRequest);
        InvocationUtils.bindInvocationToRequest(inv, httpRequest);

        // invoke the engine chain
        this.inv = inv;
        Throwable error = null;
        try {
            Object instuction = ((EngineChain) this).doNext();
            if (":continue".equals(instuction)) {
                return false;
            }
        } catch (Throwable local) {
            error = local;
            throw local;
        } finally {
            for (AfterCompletion task : afterCompletions) {
                try {
                    task.afterCompletion(inv, error);
                } catch (Throwable e) {
                    logger.error("", e);
                }
            }
            if (originalThreadRequest != null) {
                InvocationUtils.bindRequestToCurrentThread(originalThreadRequest);
            } else {
                InvocationUtils.unindRequestFromCurrentThread();
            }
            // 更新绑定的invocation,只对于那些forward后request.setAttibute影响了前者的有效。(include的不用处理了,已经做了snapshot了)
            if (preInvocation != null) {
                InvocationUtils.bindInvocationToRequest(preInvocation, httpRequest);
            }
        }

        return true;
    }

    private LinkedEngine select(LinkedEngine[] engines) {
        LinkedEngine selectedEngine = null;
        int score = 0;

        for (LinkedEngine engine : engines) {
            int candidate = engine.isAccepted(this.originalHttpRequest);
            if (logger.isDebugEnabled()) {
                logger.debug("Score of " + engine.getClass().getName() + ":" + candidate);
            }
            if (candidate > score) {
                selectedEngine = engine;
                score = candidate;
            }
        }
        if (logger.isDebugEnabled()) {

            if (selectedEngine == null) {
                logger.debug("No engine selected.");
            } else {
                String msg;
                if (selectedEngine.getTarget() instanceof ActionEngine) {
                    ActionEngine actionEngine = (ActionEngine) selectedEngine.getTarget();
                    msg = actionEngine.getController().getClass().getName() + "#"
                            + actionEngine.getMethod().getName();
                } else {
                    msg = selectedEngine.toString();
                }
                logger.debug("Engine selected:" + msg);
            }
        }
        return selectedEngine;
    }

    @Override
    public void addAfterCompletion(AfterCompletion task) {
        afterCompletions.addFirst(task);
    }
}
TOP

Related Classes of net.paoding.rose.web.impl.thread.Rose

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.