001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.web.handler;
017
018import com.google.common.base.Splitter;
019import com.jfinal.aop.Invocation;
020import com.jfinal.core.Action;
021import com.jfinal.core.ActionException;
022import com.jfinal.core.CPI;
023import com.jfinal.core.Controller;
024import com.jfinal.log.Log;
025import com.jfinal.render.RenderException;
026import io.jboot.components.valid.ValidException;
027import io.jboot.web.controller.JbootControllerContext;
028import io.jboot.web.session.JbootServletRequestWrapper;
029
030import javax.servlet.http.HttpServletRequest;
031import javax.servlet.http.HttpServletResponse;
032import java.util.Collections;
033import java.util.Map;
034import java.util.TreeMap;
035
036/**
037 * @author 没牙的小朋友 (mjl@nxu.edu.cn)
038 * @version V1.0
039 */
040public class PathVariableActionHandler extends JbootActionHandler {
041
042    private static final Log LOG = Log.getLog(PathVariableActionHandler.class);
043
044    /**
045     * handle
046     * 1: Action action = actionMapping.getAction(target)
047     * 2: new Invocation(...).invoke()
048     * 3: render(...)
049     */
050    @Override
051    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
052        if (target.lastIndexOf('.') != -1) {
053            return;
054        }
055
056        isHandled[0] = true;
057        //urlPara数组增加第2个元素存储路径参数
058        String[] urlPara = {null, null};
059        Action action = getAction(target, urlPara, request);
060
061        if (action == null) {
062            if (LOG.isWarnEnabled()) {
063                String qs = request.getQueryString();
064                LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
065            }
066            renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render();
067            return;
068        }
069
070
071        Controller controller = null;
072        try {
073            controller = controllerFactory.getController(action.getControllerClass());
074            //controller.init(request, response, urlPara[0]);
075            //存在封装的路径参数
076            if (urlPara[1] != null) {
077                Map<String, String> params = Splitter.on("&").withKeyValueSeparator("=").split(urlPara[1]);
078                PathVariableWrappedRequest wrappedRequest = new PathVariableWrappedRequest(request, response, params);
079                CPI._init_(controller, action, wrappedRequest, response, urlPara[0]);
080            } else {
081                CPI._init_(controller, action, request, response, urlPara[0]);
082            }
083            JbootControllerContext.hold(controller);
084
085            //Invocation invocation = new Invocation(action, controller);
086            Invocation invocation = getInvocation(action, controller);
087
088            if (JbootActionReporter.isReportEnable()) {
089                long time = System.currentTimeMillis();
090                try {
091                    doStartRender(target, action, controller, invocation, isHandled);
092                } finally {
093                    JbootActionReporter.report(target, controller, action, invocation, time);
094                }
095            } else {
096                doStartRender(target, action, controller, invocation, isHandled);
097            }
098
099            doAfterRender(action, controller);
100
101        } catch (RenderException e) {
102            if (LOG.isErrorEnabled()) {
103                String qs = request.getQueryString();
104                LOG.error(qs == null ? target : target + "?" + qs, e);
105            }
106        } catch (ActionException e) {
107            handleActionException(target, request, response, action, e);
108        } catch (ValidException e) {
109            handleValidException(target, request, response, action, e);
110        } catch (Exception e) {
111            handleException(target, request, response, action, e);
112        } finally {
113            JbootControllerContext.release();
114            controllerFactory.recycle(controller);
115        }
116    }
117
118
119//    private void doStartRender(String target
120//            , HttpServletRequest request
121//            , HttpServletResponse response
122//            , boolean[] isHandled
123//            , Action action
124//            , Controller controller
125//            , Invocation invocation) {
126//
127//        invocation.invoke();
128//
129//        Render render = controller.getRender();
130//        if (render instanceof ForwardActionRender) {
131//            String actionUrl = ((ForwardActionRender) render).getActionUrl();
132//            if (target.equals(actionUrl)) {
133//                throw new RuntimeException("The forward action url is the same as before.");
134//            } else {
135//                handle(actionUrl, request, response, isHandled);
136//            }
137//        } else {
138//            if (render == null && void.class != action.getMethod().getReturnType()
139//                    && renderManager.getRenderFactory() instanceof JbootRenderFactory) {
140//
141//                JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory();
142//                JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue());
143//                String forwardTo = returnValueRender.getForwardTo();
144//                if (forwardTo != null) {
145//                    handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled);
146//                    return;
147//                } else {
148//                    render = returnValueRender;
149//                }
150//            }
151//
152//            if (render == null) {
153//                render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName());
154//            }
155//
156//            render.setContext(request, response, action.getViewPath()).render();
157//        }
158//    }
159
160    /**
161     * 请求包装类用于将路径变量的URL中的额外参数加入request中
162     */
163    private class PathVariableWrappedRequest extends JbootServletRequestWrapper {
164        private final Map<String, String[]> modifiableParameters;
165        private Map<String, String[]> allParameters = null;
166
167        public PathVariableWrappedRequest(HttpServletRequest request, HttpServletResponse response,
168                                          Map<String, String> params) {
169            super(request, response);
170            modifiableParameters = new TreeMap<>();
171            params.keySet().forEach(k -> {
172                modifiableParameters.put(k, new String[]{params.get(k)});
173            });
174        }
175
176        @Override
177        public Map<String, String[]> getParameterMap() {
178            if (allParameters == null) {
179                allParameters = new TreeMap<String, String[]>();
180                allParameters.putAll(super.getParameterMap());
181                allParameters.putAll(modifiableParameters);
182            }
183            return Collections.unmodifiableMap(allParameters);
184        }
185
186        @Override
187        public String getParameter(final String name) {
188            String[] strings = getParameterMap().get(name);
189            if (strings != null) {
190                return strings[0];
191            }
192            return super.getParameter(name);
193        }
194    }
195}