Package com.alibaba.citrus.util.internal.webpagelite

Source Code of com.alibaba.citrus.util.internal.webpagelite.RequestProcessor$Runnable

/*
* Copyright (c) 2002-2012 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.util.internal.webpagelite;

import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static com.alibaba.citrus.util.internal.webpagelite.ServletRequestContext.*;
import static com.alibaba.citrus.util.io.StreamUtil.*;
import static javax.servlet.http.HttpServletResponse.*;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.citrus.util.templatelite.Template;
import com.alibaba.citrus.util.templatelite.TextWriter;

/**
* 一个简单的、利用templatelite.<code>Template</code>模板生成WEB页面及相关资源的工具类。
*
* @author Michael Zhou
*/
public abstract class RequestProcessor<RC extends RequestContext> implements PageComponentRegistry {
    private final static String DEFAULT_CONTENT_TYPE = "application/octet-stream";
    private final static Map<String, String> contentTypes;
    private final Map<String, PageComponent> components = createTreeMap(new Comparator<String>() {
        public int compare(String s1, String s2) {
            int lenDiff = s2.length() - s1.length();

            if (lenDiff != 0) {
                return lenDiff; // 先按名称长度倒排序
            } else {
                return s1.compareTo(s2); // 再按字母顺序排序
            }
        }
    });

    static {
        contentTypes = createHashMap();

        contentTypes.put("css", "text/css");
        contentTypes.put("js", "application/javascript");
        contentTypes.put("html", "text/html");
        contentTypes.put("htm", "text/html");
        contentTypes.put("xml", "text/xml");
        contentTypes.put("txt", "text/plain");
        contentTypes.put("gif", "image/gif");
        contentTypes.put("jpg", "image/jpeg");
        contentTypes.put("png", "image/png");
        contentTypes.put("ico", "image/x-icon");
    }

    /** 注册一个组件。 */
    public void register(String componentPath, PageComponent component) {
        assertNotNull(componentPath, "componentPath is null");
        assertTrue(componentPath.length() == 0 || !componentPath.startsWith("/") && componentPath.endsWith("/"),
                   "invalid componentPath: %s", componentPath);
        assertTrue(!components.containsKey(componentPath), "duplicated component: %s", componentPath);

        components.put(componentPath, component);
    }

    /** 取得所有的componentPaths。 */
    public String[] getComponentPaths() {
        return components.keySet().toArray(new String[components.size()]);
    }

    /** 取得指定名称的组件。 */
    public <PC extends PageComponent> PC getComponent(String componentPath, Class<PC> componentClass) {
        componentPath = PageComponent.normalizeComponentPath(componentPath);

        PageComponent component = assertNotNull(components.get(componentPath), "Component not found: %s", componentPath);

        if (componentClass != null) {
            return componentClass.cast(component);
        } else {
            @SuppressWarnings("unchecked")
            PC pc = (PC) component;
            return pc;
        }
    }

    /** 处理请求。 */
    public final void processRequest(final RC request) throws IOException {
        final String resourceName = request.getResourceName();

        // 先看看是不是css、js之类的资源文件,或者资源文件模板
        URL tmpres = getRawResource(resourceName);
        final boolean template;

        if (tmpres == null) {
            tmpres = getRawResource(resourceName + ".tpl");
            template = true;
        } else {
            template = false;
        }

        final URL resource = tmpres;

        if (resource != null && !resource.getPath().endsWith("/")) {
            checkLastModified(request, getLastModifiedOfResource(request, resource, template), new Runnable() {
                public void run() throws IOException {
                    if (beforeRenderingResource(request, resource, template)) {
                        renderResource(request, resource, template);
                    }
                }
            });

            return;
        }

        // 查找resource
        boolean found = resourceExists(resourceName);

        if (!found && !resourceName.endsWith("/")) {
            String indexPage = resourceName + "/";

            if (resourceExists(indexPage)) {
                request.redirectTo(request.getResourceURL(indexPage)); // 重定向到目录的正规形式
                return;
            }
        }

        // 报错:资源找不到
        if (!found) {
            request.resourceNotFound(resourceName);
            return;
        }

        // 渲染页面
        checkLastModified(request, getLastModifiedOfPage(request, resourceName), new Runnable() {
            public void run() throws IOException {
                if (beforeRenderingPage(request, resourceName)) {
                    renderPage(request, resourceName);
                }
            }
        });
    }

    private void checkLastModified(RC request, long lastModified, Runnable runner) throws IOException {
        ServletRequestContext servletRequest = getServletRequestContext(request);

        // 假如:
        // 1. request为servlet request;
        //    并且,lastModified >= 0;
        //    并且,GET方法;
        // 2. 或者,lastModified > ifModifiedSince
        // 执行runner
        if (lastModified < 0 || servletRequest == null
            || !"get".equalsIgnoreCase(servletRequest.getRequest().getMethod())) {
            runner.run();
        } else {
            long ifModifiedSince = servletRequest.getRequest().getDateHeader("If-Modified-Since");

            if (ifModifiedSince < lastModified / 1000 * 1000) {
                if (lastModified >= 0) {
                    servletRequest.getResponse().setDateHeader("Last-Modified", lastModified);
                }

                runner.run();
            } else {
                servletRequest.getResponse().setStatus(SC_NOT_MODIFIED);
            }
        }
    }

    /** 判断资源是否存在。 */
    protected abstract boolean resourceExists(String resourceName);

    /**
     * 查找指定名称的资源URL,如果没找到,就返回<code>null</code>
     * <p>
     * 先查找所有注册的component,再查找当前类同级目录的classpath。
     * </p>
     */
    private URL getRawResource(String resourceName) {
        for (String componentPath : components.keySet()) {
            if (resourceName.startsWith(componentPath)) {
                String componentResourceName = resourceName.substring(componentPath.length());
                URL resource = components.get(componentPath).getClass().getResource(componentResourceName);

                if (resource == null) {
                    resource = getClass().getResource(componentResourceName);
                }

                return resource;
            }
        }

        URL resource = null;
        Set<String> visitedPackages = createHashSet();

        for (Class<?> processorClass = getClass(); processorClass != null
                                                   && RequestProcessor.class.isAssignableFrom(processorClass); processorClass = processorClass
                .getSuperclass()) {
            String processorPackage = processorClass.getPackage().getName();

            if (visitedPackages.contains(processorPackage)) {
                continue;
            }

            visitedPackages.add(processorPackage);
            resource = processorClass.getResource(resourceName);

            if (resource != null) {
                break;
            }
        }

        return resource;
    }

    protected long getLastModifiedOfPage(RC request, String resourceName) throws IOException {
        return -1;
    }

    protected long getLastModifiedOfResource(RC request, URL resource, boolean template) throws IOException {
        if (!template) {
            return resource.openConnection().getLastModified();
        }

        return -1;
    }

    protected boolean beforeRenderingPage(RC request, String resourceName) throws IOException {
        return true;
    }

    protected boolean beforeRenderingResource(RC request, URL resource, boolean template) throws IOException {
        return true;
    }

    /** 渲染页面。 */
    protected abstract void renderPage(RC request, String resourceName) throws IOException;

    /** 渲染css、js之类的资源文件,或者资源文件模板。 */
    private void renderResource(final RC request, URL resource, boolean template) throws IOException {
        String resourceName = request.getResourceName();
        int extIndex = resourceName.indexOf(".", resourceName.lastIndexOf("/") + 1);
        String ext = extIndex > 0 ? resourceName.substring(extIndex + 1) : null;
        String contentType = defaultIfNull(contentTypes.get(ext), DEFAULT_CONTENT_TYPE);

        if (!template) {
            io(resource.openStream(), request.getOutputStream(contentType), true, true);
        } else {
            Template tpl = new Template(resource);
            PrintWriter out = request.getWriter(contentType);

            // 对于模板文件中,${url:relativeUrl}将被转换成完整的URL
            tpl.accept(new TextWriter<PrintWriter>(out) {
                @SuppressWarnings("unused")
                public void visitUrl(String relativeUrl) {
                    out().print(request.getResourceURL(relativeUrl));
                }
            });

            out.flush();
        }
    }

    /** 取得所有component的资源文件。 */
    protected List<String> getComponentResources(String ext) {
        List<String> names = createLinkedList();

        ext = trimToEmpty(ext);

        if (!ext.startsWith(".")) {
            ext = "." + ext;
        }

        for (PageComponent component : components.values()) {
            String name = component.getComponentName() + ext;

            if (component.getClass().getResource(name) != null) {
                names.add(component.getComponentPath() + name);
            }
        }

        return names;
    }

    private interface Runnable {
        void run() throws IOException;
    }
}
TOP

Related Classes of com.alibaba.citrus.util.internal.webpagelite.RequestProcessor$Runnable

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.