Package com.alibaba.citrus.turbine.util

Source Code of com.alibaba.citrus.turbine.util.ControlTool

/*
* Copyright 2010 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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 com.alibaba.citrus.turbine.util;

import static com.alibaba.citrus.springext.util.DomUtil.*;
import static com.alibaba.citrus.springext.util.SpringExtUtil.*;
import static com.alibaba.citrus.turbine.TurbineConstant.*;
import static com.alibaba.citrus.turbine.util.ControlTool.ErrorDetailLevel.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.StringEscapeUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
import org.w3c.dom.Element;

import com.alibaba.citrus.service.configuration.ProductionModeAware;
import com.alibaba.citrus.service.mappingrule.MappingRuleService;
import com.alibaba.citrus.service.moduleloader.Module;
import com.alibaba.citrus.service.moduleloader.ModuleLoaderService;
import com.alibaba.citrus.service.pull.ToolFactory;
import com.alibaba.citrus.service.requestcontext.buffered.BufferedRequestContext;
import com.alibaba.citrus.service.template.Renderable;
import com.alibaba.citrus.service.template.TemplateService;
import com.alibaba.citrus.springext.support.BeanSupport;
import com.alibaba.citrus.springext.support.parser.AbstractSingleBeanDefinitionParser;
import com.alibaba.citrus.turbine.Context;
import com.alibaba.citrus.turbine.TurbineRunDataInternal;
import com.alibaba.citrus.turbine.support.ContextAdapter;
import com.alibaba.citrus.turbine.support.MappedContext;
import com.alibaba.citrus.util.ExceptionUtil;
import com.alibaba.citrus.webx.WebxComponents;
import com.alibaba.citrus.webx.WebxException;

/**
* ���ú���ʾһ��control module��tool��
*
* @author Michael Zhou
*/
public class ControlTool extends ControlToolConfiguration implements Renderable {
    private static final Logger log = LoggerFactory.getLogger(ControlTool.class);
    private final ErrorHandler errorHandler;
    private LinkedList<ControlParameters> controlParameterStack = createLinkedList();

    public ControlTool() {
        this((ErrorHandler) null);
    }

    public ControlTool(boolean productionMode) {
        this(productionMode ? ErrorDetailLevel.messageOnly : ErrorDetailLevel.stackTrace);
    }

    public ControlTool(ErrorDetailLevel errorDetailLevel) {
        this(errorDetailLevel == null ? null : errorDetailLevel.getHandler());
    }

    public ControlTool(ErrorHandler errorHandler) {
        if (errorHandler == null) {
            errorHandler = messageOnly.getHandler();
        }

        this.errorHandler = errorHandler;
    }

    public ErrorHandler getErrorHandler() {
        return errorHandler;
    }

    /**
     * ����control��ģ�塣�˷�����<code>setModule</code>ֻ��ִ����һ�����򽫺��Ժ��ߡ�
     *
     * @param template controlģ����
     * @return <code>ControlTool</code>�����Է���ģ���еIJ���
     */
    public ControlTool setTemplate(String template) {
        ControlParameters params = getControlParameters();

        if (params.module == null) {
            params.template = template;
        }

        return this;
    }

    /**
     * ����control��ģ�顣�˷�����<code>setTemplate</code>ֻ��ִ����һ�����򽫺��Ժ��ߡ�
     *
     * @param module controlģ����
     * @return <code>ControlTool</code>�����Է���ģ���еIJ���
     */
    public ControlTool setModule(String module) {
        ControlParameters params = getControlParameters();

        if (params.template == null) {
            params.module = module;
        }

        return this;
    }

    /**
     * ����control�IJ�����
     * <p>
     * ��Щ��������������һ��һ���Ե�<code>Map</code>�У���render�ɹ��Ժ󣬸�map�ͱ��������Ա��ٴε��ø�control��
     * </p>
     *
     * @param name ������
     * @param value ����
     * @return <code>ControlTool</code>�����Է���ģ���еIJ���
     */
    public ControlTool setParameter(String name, Object value) {
        ControlParameters params = getControlParameters();

        params.put(name, value);

        return this;
    }

    /**
     * ��screen����ʱ������ָ�����Ƶı�����ʹ�����߿��Է��ʡ�
     */
    public ControlTool export(String... vars) {
        ControlParameters params = getControlParameters();

        params.exportVars = createHashSet(vars);

        return this;
    }

    /**
     * ��Ⱦ����
     *
     * @return ��Ⱦ�Ľ��
     */
    public String render() {
        assertInitialized();

        ControlParameters params = getControlParameters();
        String componentName;
        String target = null;
        String content;
        boolean isTemplate;

        try {
            if (params.template != null) {
                componentName = parseComponentName(params.template);
                target = parseLocalName(params.template);
                isTemplate = true;
            } else if (params.module != null) {
                componentName = parseComponentName(params.module);
                target = parseLocalName(params.module);
                isTemplate = false;
            } else {
                throw new IllegalArgumentException("Neither template nor module name was specified to render a control");
            }

            // controlTool֧�ֿ�component���ã�����ȡ��ָ��component�µ�service��
            ModuleLoaderService moduleLoaderService = getService("moduleLoaderService", componentName,
                    this.moduleLoaderService, ModuleLoaderService.class);

            MappingRuleService mappingRuleService = getService("mappingRuleService", componentName,
                    this.mappingRuleService, MappingRuleService.class);

            TemplateService templateService = getService("templateService", componentName, this.templateService,
                    TemplateService.class);

            // ȡ��ʵ�ʵ�template/module����
            String templateName = null;
            String moduleName = null;

            if (isTemplate) {
                templateName = target;
                moduleName = mappingRuleService.getMappedName(CONTROL_MODULE, target);
            } else {
                moduleName = mappingRuleService.getMappedName(CONTROL_MODULE_NO_TEMPLATE, target);
            }

            // ִ��control module
            Module controlModule;

            if (templateName == null) {
                // templateNameδָ��ʱ��������module����û�����׳�ModuleNotFoundException
                controlModule = moduleLoaderService.getModule(CONTROL_MODULE, moduleName);
            } else {
                // ��ָ����templateNameʱ������û�е�control module����������Ⱦģ�塣
                // ������ʵ����page-driven������дģ�壬��Ҫʱ��дһ��module class��֮��Ӧ��
                controlModule = moduleLoaderService.getModuleQuiet(CONTROL_MODULE, moduleName);
            }

            if (log.isTraceEnabled()) {
                if (templateName != null) {
                    log.trace("Rendering control: template=" + templateName + ", control=" + moduleName);
                } else {
                    log.trace("Rendering control without template: control=" + moduleName);
                }
            }

            // ���ò���
            this.bufferedRequestContext.pushBuffer();

            try {
                controlParameterStack.addFirst(new ControlParameters()); // ֧��control��Ƕ��

                TurbineRunDataInternal rundata = (TurbineRunDataInternal) TurbineUtil.getTurbineRunData(this.request);
                Context contextForControl = createContextForControl(params, componentName);

                rundata.pushContext(contextForControl, templateName);

                try {
                    if (controlModule != null) {
                        controlModule.execute();
                    }

                    // Control module����ͨ��ע��ControlParameters�ӿ����޸�template��
                    String templateOverriden = rundata.getControlTemplate();

                    if (!isEquals(templateOverriden, templateName)) {
                        log.debug("Control template has been changed by module: " + templateName + " -> "
                                + templateOverriden);

                        templateName = templateOverriden;
                    }

                    if (templateName != null) {
                        templateName = mappingRuleService.getMappedName(CONTROL_TEMPLATE, templateName);
                    }

                    if (templateName != null) {
                        templateService.writeTo(templateName, new ContextAdapter(contextForControl), rundata
                                .getResponse().getWriter());
                    }
                } finally {
                    rundata.popContext();
                }
            } finally {
                controlParameterStack.removeFirst();
                content = this.bufferedRequestContext.popCharBuffer();
            }
        } catch (Exception e) {
            content = null;

            try {
                content = errorHandler.handleException(target, e);
            } catch (RuntimeException ee) {
            }

            // ���handler���ؿգ����׳��쳣���������handler���ص����ݡ�
            if (content == null) {
                throw new WebxException("Failed to execute control module: " + target, e);
            } else {
                log.error("Failed to execute control module: " + target, e);
            }
        } finally {
            // ����������Ա�����
            params.template = null;
            params.module = null;
            params.exportVars = null;
            params.clear();
        }

        return content;
    }

    /**
     * ����template��module������ǰ�����硰componentName:��ǰ׺���򷵻ش�componentName�����򷵻�null��
     */
    private String parseComponentName(String name) {
        int index = name == null ? -1 : name.indexOf(":");

        if (index >= 0) {
            return trimToNull(name.substring(0, index));
        }

        return null;
    }

    /**
     * ȡ�ò�������componentName:��ǰ׺��template��module�����ơ�
     */
    private String parseLocalName(String name) {
        int index = name == null ? -1 : name.indexOf(":");

        if (index >= 0) {
            return trimToNull(name.substring(index + 1));
        } else {
            return trimToNull(name);
        }
    }

    private <T> T getService(String name, String componentName, T defaultService, Class<T> serviceType) {
        if (componentName == null) {
            return defaultService;
        }

        ApplicationContext context = assertNotNull(this.components.getComponent(componentName),
                "invalid prefix \"%s:\", component does not exist", componentName).getApplicationContext();

        try {
            return serviceType.cast(context.getBean(name, serviceType));
        } catch (BeansException e) {
            throw new IllegalArgumentException(String.format("Could not get service: \"%s:%s\"", componentName,
                    serviceType.getSimpleName()), e);
        }
    }

    private Context createContextForControl(ControlParameters params, String componentName) {
        // get parent context
        TurbineRunDataInternal rundata = (TurbineRunDataInternal) TurbineUtil.getTurbineRunData(this.request);
        Context screenContext = rundata.getContext(componentName);
        final Context callerContext = rundata.getCurrentContext();
        final Set<String> exportVars = params.exportVars == null ? Collections.<String> emptySet() : params.exportVars;

        // create control context
        MappedContext context = new MappedContext(screenContext) {
            @Override
            protected void internalPut(String key, Object value) {
                if (isExport(key)) {
                    callerContext.put(key, value);
                }

                super.internalPut(key, value);
            }

            @Override
            protected void internalRemove(String key) {
                if (isExport(key)) {
                    callerContext.remove(key);
                }

                super.internalRemove(key);
            }

            private boolean isExport(String key) {
                return callerContext != null && (exportAll || exportVars.contains(key));
            }
        };

        // add all params
        context.getMap().putAll(params);

        return context;
    }

    /**
     * ȡ��ջ����control������
     */
    protected ControlParameters getControlParameters() {
        if (controlParameterStack.isEmpty()) {
            controlParameterStack.addFirst(new ControlParameters());
        }

        return controlParameterStack.getFirst();
    }

    /**
     * ����һ��control�ĵ��ò�����
     */
    protected static class ControlParameters extends HashMap<String, Object> {
        private static final long serialVersionUID = 3256721796996084529L;
        private String module;
        private String template;
        private Set<String> exportVars;

        public ControlParameters() {
            super(4);
        }
    }

    /**
     * ��ӡ�쳣����ϸ�̶ȡ�
     */
    public static enum ErrorDetailLevel {
        /**
         * �׳��쳣��
         */
        throwException,
        /**
         * ������κδ������ݡ�
         */
        quiet,
        /**
         * ֻ����쳣��Ϣ��
         */
        messageOnly,
        /**
         * ����쳣����ϸ��Ϣ��
         */
        stackTrace,
        /**
         * ���쳣��ӡ��HTMLע���С�
         */
        comment;

        private final ErrorHandler handler = new DefaultErrorHandler(this);

        public ErrorHandler getHandler() {
            return handler;
        }
    }

    /**
     * ��������controlִ�й����е��쳣��
     */
    public static interface ErrorHandler {
        String handleException(String controlTarget, Exception e);
    }

    public static class ThrowError implements ErrorHandler {
        public String handleException(String controlTarget, Exception e) {
            return null;
        }
    }

    public static class DefaultErrorHandler implements ErrorHandler {
        private String errorTagClass;
        private ErrorDetailLevel detailLevel;

        public DefaultErrorHandler() {
        }

        public DefaultErrorHandler(ErrorDetailLevel detailLevel) {
            this.detailLevel = detailLevel;
        }

        public String getErrorTagClass() {
            return defaultIfNull(errorTagClass, "webx.error");
        }

        public void setErrorTagClass(String errorTagClass) {
            this.errorTagClass = trimToNull(errorTagClass);
        }

        public ErrorDetailLevel getDetailLevel() {
            return detailLevel == null ? messageOnly : detailLevel;
        }

        public void setDetailLevel(ErrorDetailLevel detailLevel) {
            this.detailLevel = detailLevel;
        }

        public String handleException(String controlTarget, Exception e) {
            ErrorDetailLevel detailLevel = getDetailLevel();

            switch (detailLevel) {
                case throwException:
                    return null;

                case quiet:
                    return EMPTY_STRING;

                default:
                    break;
            }

            StringWriter buf = new StringWriter();
            PrintWriter pw = new PrintWriter(buf);
            Formatter fmt = new Formatter(pw);

            fmt.format("<!-- control failed: target=%s, exceptionType=%s -->", controlTarget, e.getClass().getName());

            switch (detailLevel) {
                case messageOnly:
                    fmt.format("<div class=\"%s\">", getErrorTagClass());

                    String msg = e.getMessage();

                    if (isEmpty(msg)) {
                        msg = e.getClass().getSimpleName();
                    }

                    pw.append(escapeHtml(msg)); // !!��Ҫ��escapeHtml

                    pw.append("</div>");
                    break;

                case stackTrace:
                    fmt.format("<div class=\"%s\">", getErrorTagClass());
                    pw.append(ExceptionUtil.getStackTraceForHtmlComment(e)); // !!��Ҫ��escapeHtml
                    pw.append("</div>");
                    break;

                case comment:
                    pw.append("<!-- stacktrace: \n");
                    pw.append(ExceptionUtil.getStackTraceForHtmlComment(e)); // !!��Ҫ��escapeHtml
                    pw.append("-->");
                    break;

                default:
                    unreachableCode(detailLevel.name());
            }

            pw.flush();
            return buf.toString();
        }
    }

    public static class DefinitionParser extends AbstractSingleBeanDefinitionParser<Factory> {
        @Override
        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
            String errorDetailLevel = trimToNull(element.getAttribute("detailLevel"));

            if (errorDetailLevel != null) {
                builder.addPropertyValue("errorDetailLevel", errorDetailLevel);
            } else {
                Element errorHandlerElement = theOnlySubElement(element, and(sameNs(element), name("errorHandler")));

                if (errorHandlerElement != null) {
                    builder.addPropertyValue("errorHandler",
                            parseBean(errorHandlerElement, parserContext, builder.getRawBeanDefinition()));
                }
            }

            String exportAll = trimToNull(element.getAttribute("exportAll"));

            if (exportAll != null) {
                builder.addPropertyValue("exportAll", exportAll);
            }
        }
    }

    /**
     * pull tool factory��
     */
    public static class Factory extends ControlToolConfiguration implements ToolFactory, ProductionModeAware {
        private ErrorDetailLevel errorDetailLevel;
        private ErrorHandler errorHandler;
        private boolean productionMode = true;
        private boolean exportAll;

        public void setProductionMode(boolean productionMode) {
            this.productionMode = productionMode;
        }

        public void setErrorDetailLevel(ErrorDetailLevel errorDetailLevel) {
            this.errorDetailLevel = errorDetailLevel;
        }

        public void setErrorHandler(ErrorHandler errorHandler) {
            this.errorHandler = errorHandler;
        }

        public void setExportAll(boolean exportAll) {
            this.exportAll = exportAll;
        }

        public boolean isSingleton() {
            return false;
        }

        public Object createTool() throws Exception {
            ControlTool tool;

            if (errorDetailLevel != null) {
                tool = new ControlTool(errorDetailLevel);
            } else if (errorHandler != null) {
                tool = new ControlTool(errorHandler);
            } else {
                tool = new ControlTool(productionMode);
            }

            tool.init(components, moduleLoaderService, mappingRuleService, templateService, request,
                    bufferedRequestContext);

            tool.exportAll = exportAll;
            tool.afterPropertiesSet();

            return tool;
        }
    }
}

class ControlToolConfiguration extends BeanSupport {
    protected WebxComponents components;
    protected ModuleLoaderService moduleLoaderService;
    protected MappingRuleService mappingRuleService;
    protected TemplateService templateService;
    protected HttpServletRequest request;
    protected BufferedRequestContext bufferedRequestContext;
    protected boolean exportAll;

    @Autowired
    protected void init(WebxComponents components, ModuleLoaderService moduleLoaderService,
                        MappingRuleService mappingRuleService, TemplateService templateService,
                        HttpServletRequest request, BufferedRequestContext bufferedRequestContext) {
        this.components = components;
        this.moduleLoaderService = moduleLoaderService;
        this.mappingRuleService = mappingRuleService;
        this.templateService = templateService;
        this.request = request;
        this.bufferedRequestContext = bufferedRequestContext;
    }

    @Override
    protected void init() {
        assertNotNull(components, "no components");
        assertNotNull(moduleLoaderService, "no moduleLoaderService");
        assertNotNull(mappingRuleService, "no mappingRuleService");
        assertNotNull(templateService, "no templateService");
        assertNotNull(request, "no request");
        assertNotNull(bufferedRequestContext, "no bufferedRequestContext");
    }
}
TOP

Related Classes of com.alibaba.citrus.turbine.util.ControlTool

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.