net.ymate.platform.webmvc.WebMVC.java Source code

Java tutorial

Introduction

Here is the source code for net.ymate.platform.webmvc.WebMVC.java

Source

/*
 * Copyright 2007-2016 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.ymate.platform.webmvc;

import net.ymate.platform.core.Version;
import net.ymate.platform.core.YMP;
import net.ymate.platform.core.beans.BeanMeta;
import net.ymate.platform.core.lang.PairObject;
import net.ymate.platform.core.module.IModule;
import net.ymate.platform.core.module.annotation.Module;
import net.ymate.platform.core.util.ClassUtils;
import net.ymate.platform.core.util.RuntimeUtils;
import net.ymate.platform.webmvc.annotation.*;
import net.ymate.platform.webmvc.base.Type;
import net.ymate.platform.webmvc.context.WebContext;
import net.ymate.platform.webmvc.handle.ControllerHandler;
import net.ymate.platform.webmvc.handle.InterceptorRuleHandler;
import net.ymate.platform.webmvc.impl.DefaultInterceptorRuleProcessor;
import net.ymate.platform.webmvc.impl.DefaultModuleCfg;
import net.ymate.platform.webmvc.impl.NullWebCacheProcessor;
import net.ymate.platform.webmvc.support.MultipartRequestWrapper;
import net.ymate.platform.webmvc.support.RequestExecutor;
import net.ymate.platform.webmvc.support.RequestMappingParser;
import net.ymate.platform.webmvc.view.IView;
import net.ymate.platform.webmvc.view.View;
import net.ymate.platform.webmvc.view.impl.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * MVC?
 *
 * @author  (suninformation@163.com) on 2012-12-7 ?10:23:39
 * @version 1.0
 */
@Module
public class WebMVC implements IModule, IWebMvc {

    public static final Version VERSION = new Version(2, 0, 0, WebMVC.class.getPackage().getImplementationVersion(),
            Version.VersionType.GA);

    private final Log _LOG = LogFactory.getLog(WebMVC.class);

    private static volatile IWebMvc __instance;

    private YMP __owner;

    private IWebMvcModuleCfg __moduleCfg;

    private boolean __inited;

    private RequestMappingParser __mappingParser;

    private IInterceptorRuleProcessor __interceptorRuleProcessor;

    /**
     * @return MVC?
     */
    public static IWebMvc get() {
        if (__instance == null) {
            synchronized (VERSION) {
                if (__instance == null) {
                    __instance = YMP.get().getModule(WebMVC.class);
                }
            }
        }
        return __instance;
    }

    /**
     * @param owner YMP?
     * @return YMP?MVC?
     */
    public static IWebMvc get(YMP owner) {
        return owner.getModule(WebMVC.class);
    }

    public String getName() {
        return IWebMvc.MODULE_NAME;
    }

    public void init(YMP owner) throws Exception {
        if (!__inited) {
            //
            _LOG.info("Initializing ymate-platform-webmvc-" + VERSION);
            //
            __owner = owner;
            __moduleCfg = new DefaultModuleCfg(owner);
            __mappingParser = new RequestMappingParser();
            __owner.getEvents().registerEvent(WebEvent.class);
            __owner.registerHandler(Controller.class, new ControllerHandler(this));
            if (__moduleCfg.isConventionInterceptorMode()) {
                __interceptorRuleProcessor = new DefaultInterceptorRuleProcessor();
                __interceptorRuleProcessor.init(this);
                __owner.registerHandler(InterceptorRule.class, new InterceptorRuleHandler(this));
            }
            //
            __inited = true;
        }
    }

    public boolean isInited() {
        return __inited;
    }

    public void destroy() throws Exception {
        if (__inited) {
            __inited = false;
            //
            __owner = null;
        }
    }

    public IWebMvcModuleCfg getModuleCfg() {
        return __moduleCfg;
    }

    public YMP getOwner() {
        return __owner;
    }

    public boolean registerController(Class<? extends Controller> targetClass) throws Exception {
        boolean _isValid = false;
        for (Method _method : targetClass.getDeclaredMethods()) {
            if (_method.isAnnotationPresent(RequestMapping.class)) {
                RequestMeta _meta = new RequestMeta(this, targetClass, _method);
                __mappingParser.registerRequestMeta(_meta);
                //
                if (__owner.getConfig().isDevelopMode()) {
                    _LOG.debug("--> " + _meta.getAllowMethods() + ": " + _meta.getMapping() + " : "
                            + _meta.getTargetClass().getName());
                }
                //
                _isValid = true;
            }
        }
        //
        if (_isValid) {
            if (targetClass.getAnnotation(Controller.class).singleton()) {
                __owner.registerBean(BeanMeta.create(targetClass.newInstance(), targetClass));
            } else {
                __owner.registerBean(BeanMeta.create(targetClass));
            }
        }
        return _isValid;
    }

    public boolean registerInterceptorRule(Class<? extends IInterceptorRule> targetClass) throws Exception {
        if (__interceptorRuleProcessor != null) {
            __interceptorRuleProcessor.registerInterceptorRule(targetClass);
            return true;
        }
        return false;
    }

    private IWebCacheProcessor __doGetWebCacheProcessor(ResponseCache responseCache) {
        IWebCacheProcessor _cacheProcessor = null;
        if (responseCache != null) {
            if (!NullWebCacheProcessor.class.equals(responseCache.processorClass())) {
                _cacheProcessor = ClassUtils.impl(responseCache.processorClass(), IWebCacheProcessor.class);
            }
            if (_cacheProcessor == null) {
                _cacheProcessor = getModuleCfg().getCacheProcessor();
            }
        }
        return _cacheProcessor;
    }

    public void processRequest(IRequestContext context, ServletContext servletContext, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        StopWatch _consumeTime = null;
        long _threadId = Thread.currentThread().getId();
        try {
            if (__owner.getConfig().isDevelopMode()) {
                _consumeTime = new StopWatch();
                _consumeTime.start();
            }
            _LOG.debug("--> [" + _threadId + "] Process request start: " + context.getHttpMethod() + ":"
                    + context.getRequestMapping());
            //
            RequestMeta _meta = __mappingParser.doParse(context);
            if (_meta != null) {
                //
                _LOG.debug("--- [" + _threadId + "] Request mode: controller");
                // ????
                if (_meta.allowHttpMethod(context.getHttpMethod())) {
                    // ?
                    Map<String, String> _allowMap = _meta.getAllowHeaders();
                    for (Map.Entry<String, String> _entry : _allowMap.entrySet()) {
                        String _value = WebContext.getRequest().getHeader(_entry.getKey());
                        if (StringUtils.trimToEmpty(_entry.getValue()).equals("*")) {
                            if (StringUtils.isBlank(_value)) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                                //
                                _LOG.debug("--- [" + _threadId + "] Check request allowed: NO");
                                return;
                            }
                        } else {
                            if (_value == null || !_value.equalsIgnoreCase(_entry.getValue())) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                                //
                                _LOG.debug("--- [" + _threadId + "] Check request allowed: NO");
                                return;
                            }
                        }
                    }
                    // ??
                    _allowMap = _meta.getAllowParams();
                    for (Map.Entry<String, String> _entry : _allowMap.entrySet()) {
                        if (StringUtils.trimToEmpty(_entry.getValue()).equals("*")) {
                            if (!WebContext.getRequest().getParameterMap().containsKey(_entry.getKey())) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                                //
                                _LOG.debug("--- [" + _threadId + "] Check request allowed: NO");
                                return;
                            }
                        } else {
                            String _value = WebContext.getRequest().getParameter(_entry.getKey());
                            if (_value == null || !_value.equalsIgnoreCase(_entry.getValue())) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                                //
                                _LOG.debug("--- [" + _threadId + "] Check request allowed: NO");
                                return;
                            }
                        }
                    }
                    // ???
                    if (context.getHttpMethod().equals(Type.HttpMethod.POST)
                            && _meta.getMethod().isAnnotationPresent(FileUpload.class)) {
                        if (!(request instanceof IMultipartRequestWrapper)) {
                            // ?????
                            request = new MultipartRequestWrapper(this, request);
                        }
                        //
                        _LOG.debug("--- [" + _threadId + "] Include file upload: YES");
                    }
                    WebContext.getContext().addAttribute(Type.Context.HTTP_REQUEST, request);
                    //
                    IWebCacheProcessor _cacheProcessor = __doGetWebCacheProcessor(_meta.getResponseCache());
                    IView _view = null;
                    // ??
                    if (_cacheProcessor != null) {
                        // ?
                        if (_cacheProcessor.processResponseCache(this, _meta.getResponseCache(), context, null)) {
                            // ?, 
                            _view = View.nullView();
                            //
                            _LOG.debug("--- [" + _threadId + "] Load data from the cache: YES");
                        }
                    }
                    if (_view == null) {
                        _view = RequestExecutor.bind(this, _meta).execute();
                        if (_view != null) {
                            if (_cacheProcessor != null) {
                                try {
                                    // ?
                                    if (_cacheProcessor.processResponseCache(this, _meta.getResponseCache(),
                                            context, _view)) {
                                        _view = View.nullView();
                                        //
                                        _LOG.debug("--- [" + _threadId + "] Results data cached: YES");
                                    }
                                } catch (Exception e) {
                                    // ????, 
                                    _LOG.warn(e.getMessage(), RuntimeUtils.unwrapThrow(e));
                                }
                            }
                            _view.render();
                        } else {
                            HttpStatusView.NOT_FOUND.render();
                        }
                    } else {
                        _view.render();
                    }
                } else {
                    response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                }
            } else if (__moduleCfg.isConventionMode()) {
                boolean _isAllowConvention = true;
                if (!__moduleCfg.getConventionViewNotAllowPaths().isEmpty()) {
                    for (String _vPath : __moduleCfg.getConventionViewNotAllowPaths()) {
                        if (context.getRequestMapping().startsWith(_vPath)) {
                            _isAllowConvention = false;
                            break;
                        }
                    }
                }
                if (_isAllowConvention && !__moduleCfg.getConventionViewAllowPaths().isEmpty()) {
                    _isAllowConvention = false;
                    for (String _vPath : __moduleCfg.getConventionViewAllowPaths()) {
                        if (context.getRequestMapping().startsWith(_vPath)) {
                            _isAllowConvention = true;
                            break;
                        }
                    }
                }
                if (_isAllowConvention) {
                    //
                    _LOG.debug("--- [" + _threadId + "] Request mode: convention");
                    //
                    IView _view = null;
                    ResponseCache _responseCache = null;
                    if (__interceptorRuleProcessor != null) {
                        // ?Convention
                        PairObject<IView, ResponseCache> _result = __interceptorRuleProcessor.processRequest(this,
                                context);
                        _view = _result.getKey();
                        _responseCache = _result.getValue();
                    }
                    // ??
                    IWebCacheProcessor _cacheProcessor = __doGetWebCacheProcessor(_responseCache);
                    // ??
                    if (_cacheProcessor != null) {
                        // ?
                        if (_cacheProcessor.processResponseCache(this, _responseCache, context, null)) {
                            // ?, 
                            _view = View.nullView();
                            //
                            _LOG.debug("--- [" + _threadId + "] Load data from the cache: YES");
                        }
                    }
                    if (_view == null) {
                        // ?Convention?URL??
                        String _requestMapping = context.getRequestMapping();
                        String[] _urlParamArr = getModuleCfg().isConventionUrlrewriteMode()
                                ? StringUtils.split(_requestMapping, '_')
                                : new String[] { _requestMapping };
                        if (_urlParamArr != null && _urlParamArr.length > 1) {
                            _requestMapping = _urlParamArr[0];
                            List<String> _urlParams = Arrays.asList(_urlParamArr).subList(1, _urlParamArr.length);
                            WebContext.getRequest().setAttribute("UrlParams", _urlParams);
                            //
                            _LOG.debug("--- [" + _threadId + "] With parameters : " + _urlParams);
                        }
                        //
                        if (__moduleCfg.getErrorProcessor() != null) {
                            _view = __moduleCfg.getErrorProcessor().onConvention(this, context);
                        }
                        if (_view == null) {
                            // ???URL
                            String[] _fileTypes = { ".html", ".jsp", ".ftl", ".vm" };
                            for (String _fileType : _fileTypes) {
                                File _targetFile = new File(__moduleCfg.getAbstractBaseViewPath(),
                                        _requestMapping + _fileType);
                                if (_targetFile.exists()) {
                                    if (".html".equals(_fileType)) {
                                        _view = HtmlView.bind(this, _requestMapping.substring(1));
                                        //
                                        _LOG.debug("--- [" + _threadId + "] Rendering template file : "
                                                + _requestMapping + _fileType);
                                        break;
                                    } else if (".jsp".equals(_fileType)) {
                                        _view = JspView.bind(this, _requestMapping.substring(1));
                                        //
                                        _LOG.debug("--- [" + _threadId + "] Rendering template file : "
                                                + _requestMapping + _fileType);
                                        break;
                                    } else if (".ftl".equals(_fileType)) {
                                        _view = FreemarkerView.bind(this, _requestMapping.substring(1));
                                        //
                                        _LOG.debug("--- [" + _threadId + "] Rendering template file : "
                                                + _requestMapping + _fileType);
                                        break;
                                    } else if (".vm".equals(_fileType)) {
                                        _view = VelocityView.bind(this, _requestMapping.substring(1));
                                        //
                                        _LOG.debug("--- [" + _threadId + "] Rendering template file : "
                                                + _requestMapping + _fileType);
                                    }
                                }
                            }
                        }
                        //
                        if (_view != null && _cacheProcessor != null) {
                            try {
                                if (_cacheProcessor.processResponseCache(this, _responseCache, context, _view)) {
                                    _view = View.nullView();
                                    //
                                    _LOG.debug("--- [" + _threadId + "] Results data cached: YES");
                                }
                            } catch (Exception e) {
                                // ????, 
                                _LOG.warn(e.getMessage(), RuntimeUtils.unwrapThrow(e));
                            }
                        }
                    }
                    if (_view != null) {
                        _view.render();
                    } else {
                        HttpStatusView.NOT_FOUND.render();
                    }
                } else {
                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
                }
            } else {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
            }
        } finally {
            if (_consumeTime != null && __owner.getConfig().isDevelopMode()) {
                _consumeTime.stop();
                _LOG.debug("--- [" + _threadId + "] Total execution time: " + _consumeTime.getTime() + "ms");
            }
            _LOG.debug("<-- [" + _threadId + "] Process request completed: " + context.getHttpMethod() + ":"
                    + context.getRequestMapping());
        }
    }
}