001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
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 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
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 */
016
017package org.springframework.web.portlet.handler;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import javax.portlet.PortletRequest;
023
024import org.springframework.beans.BeansException;
025import org.springframework.context.support.ApplicationObjectSupport;
026import org.springframework.core.Ordered;
027import org.springframework.web.context.request.WebRequestInterceptor;
028import org.springframework.web.portlet.HandlerExecutionChain;
029import org.springframework.web.portlet.HandlerInterceptor;
030import org.springframework.web.portlet.HandlerMapping;
031
032/**
033 * Abstract base class for {@link org.springframework.web.portlet.HandlerMapping}
034 * implementations. Supports ordering, a default handler, and handler interceptors.
035 *
036 * @author Juergen Hoeller
037 * @author John A. Lewis
038 * @since 2.0
039 * @see #getHandlerInternal
040 * @see #setDefaultHandler
041 * @see #setInterceptors
042 * @see org.springframework.web.portlet.HandlerInterceptor
043 */
044public abstract class AbstractHandlerMapping extends ApplicationObjectSupport implements HandlerMapping, Ordered {
045
046        private int order = Integer.MAX_VALUE;  // default: same as non-Ordered
047
048        private Object defaultHandler;
049
050        private final List<Object> interceptors = new ArrayList<Object>();
051
052        private boolean applyWebRequestInterceptorsToRenderPhaseOnly = true;
053
054        private HandlerInterceptor[] adaptedInterceptors;
055
056
057        /**
058         * Specify the order value for this HandlerMapping bean.
059         * <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
060         * @see org.springframework.core.Ordered#getOrder()
061         */
062        public final void setOrder(int order) {
063          this.order = order;
064        }
065
066        @Override
067        public final int getOrder() {
068          return this.order;
069        }
070
071        /**
072         * Set the default handler for this handler mapping.
073         * This handler will be returned if no specific mapping was found.
074         * <p>Default is {@code null}, indicating no default handler.
075         */
076        public void setDefaultHandler(Object defaultHandler) {
077                this.defaultHandler = defaultHandler;
078        }
079
080        /**
081         * Return the default handler for this handler mapping,
082         * or {@code null} if none.
083         */
084        public Object getDefaultHandler() {
085                return this.defaultHandler;
086        }
087
088        /**
089         * Set the interceptors to apply for all handlers mapped by this handler mapping.
090         * <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
091         * Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
092         * @param interceptors array of handler interceptors, or {@code null} if none
093         * @see #adaptInterceptor
094         * @see org.springframework.web.portlet.HandlerInterceptor
095         * @see org.springframework.web.context.request.WebRequestInterceptor
096         */
097        public void setInterceptors(Object[] interceptors) {
098                this.interceptors.addAll(Arrays.asList(interceptors));
099        }
100
101        /**
102         * Specify whether to apply WebRequestInterceptors to the Portlet render phase
103         * only ("true", or whether to apply them to the Portlet action phase as well
104         * ("false").
105         * <p>Default is "true", since WebRequestInterceptors are usually built for
106         * MVC-style handler execution plus rendering process (which is, for example,
107         * the primary target scenario for "Open Session in View" interceptors,
108         * offering lazy loading of persistent objects during view rendering).
109         * Set this to "false" to have WebRequestInterceptors apply to the action
110         * phase as well (for example, in case of an "Open Session in View" interceptor,
111         * to allow for lazy loading outside of a transaction during the action phase).
112         * @see #setInterceptors
113         * @see org.springframework.web.context.request.WebRequestInterceptor
114         * @see WebRequestHandlerInterceptorAdapter#WebRequestHandlerInterceptorAdapter(WebRequestInterceptor, boolean)
115         * @see org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor
116         */
117        public void setApplyWebRequestInterceptorsToRenderPhaseOnly(boolean applyWebRequestInterceptorsToRenderPhaseOnly) {
118                this.applyWebRequestInterceptorsToRenderPhaseOnly = applyWebRequestInterceptorsToRenderPhaseOnly;
119        }
120
121
122        /**
123         * Initializes the interceptors.
124         * @see #extendInterceptors(java.util.List)
125         * @see #initInterceptors()
126         */
127        @Override
128        protected void initApplicationContext() throws BeansException {
129                extendInterceptors(this.interceptors);
130                initInterceptors();
131        }
132
133        /**
134         * Extension hook that subclasses can override to register additional interceptors,
135         * given the configured interceptors (see {@link #setInterceptors}).
136         * <p>Will be invoked before {@link #initInterceptors()} adapts the specified
137         * interceptors into {@link HandlerInterceptor} instances.
138         * <p>The default implementation is empty.
139         * @param interceptors the configured interceptor List (never {@code null}),
140         * allowing to add further interceptors before as well as after the existing
141         * interceptors
142         */
143        protected void extendInterceptors(List<?> interceptors) {
144        }
145
146        /**
147         * Initialize the specified interceptors, adapting them where necessary.
148         * @see #setInterceptors
149         * @see #adaptInterceptor
150         */
151        protected void initInterceptors() {
152                if (!this.interceptors.isEmpty()) {
153                        this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];
154                        for (int i = 0; i < this.interceptors.size(); i++) {
155                                Object interceptor = this.interceptors.get(i);
156                                if (interceptor == null) {
157                                        throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
158                                }
159                                this.adaptedInterceptors[i] = adaptInterceptor(interceptor);
160                        }
161                }
162        }
163
164        /**
165         * Adapt the given interceptor object to the HandlerInterceptor interface.
166         * <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
167         * Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
168         * Can be overridden in subclasses.
169         * @param interceptor the specified interceptor object
170         * @return the interceptor wrapped as HandlerInterceptor
171         * @see #setApplyWebRequestInterceptorsToRenderPhaseOnly
172         * @see org.springframework.web.portlet.HandlerInterceptor
173         * @see org.springframework.web.context.request.WebRequestInterceptor
174         * @see WebRequestHandlerInterceptorAdapter
175         */
176        protected HandlerInterceptor adaptInterceptor(Object interceptor) {
177                if (interceptor instanceof HandlerInterceptor) {
178                        return (HandlerInterceptor) interceptor;
179                }
180                else if (interceptor instanceof WebRequestInterceptor) {
181                        return new WebRequestHandlerInterceptorAdapter(
182                                        (WebRequestInterceptor) interceptor, this.applyWebRequestInterceptorsToRenderPhaseOnly);
183                }
184                else {
185                        throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
186                }
187        }
188
189        /**
190         * Return the adapted interceptors as HandlerInterceptor array.
191         * @return the array of HandlerInterceptors, or {@code null} if none
192         */
193        protected final HandlerInterceptor[] getAdaptedInterceptors() {
194                return this.adaptedInterceptors;
195        }
196
197
198        /**
199         * Look up a handler for the given request, falling back to the default
200         * handler if no specific one is found.
201         * @param request current portlet request
202         * @return the corresponding handler instance, or the default handler
203         * @see #getHandlerInternal
204         */
205        @Override
206        public final HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
207                Object handler = getHandlerInternal(request);
208                if (handler == null) {
209                        handler = getDefaultHandler();
210                }
211                if (handler == null) {
212                        return null;
213                }
214                // Bean name or resolved handler?
215                if (handler instanceof String) {
216                        String handlerName = (String) handler;
217                        handler = getApplicationContext().getBean(handlerName);
218                }
219                return getHandlerExecutionChain(handler, request);
220        }
221
222        /**
223         * Look up a handler for the given request, returning {@code null} if no
224         * specific one is found. This method is called by {@link #getHandler};
225         * a {@code null} return value will lead to the default handler, if one is set.
226         * <p>Note: This method may also return a pre-built {@link HandlerExecutionChain},
227         * combining a handler object with dynamically determined interceptors.
228         * Statically specified interceptors will get merged into such an existing chain.
229         * @param request current portlet request
230         * @return the corresponding handler instance, or {@code null} if none found
231         * @throws Exception if there is an internal error
232         * @see #getHandler
233         */
234        protected abstract Object getHandlerInternal(PortletRequest request) throws Exception;
235
236        /**
237         * Build a HandlerExecutionChain for the given handler, including applicable interceptors.
238         * <p>The default implementation simply builds a standard HandlerExecutionChain with
239         * the given handler and this handler mapping's common interceptors. Subclasses may
240         * override this in order to extend/rearrange the list of interceptors.
241         * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a pre-built
242         * HandlerExecutionChain. This method should handle those two cases explicitly,
243         * either building a new HandlerExecutionChain or extending the existing chain.
244         * <p>For simply adding an interceptor, consider calling {@code super.getHandlerExecutionChain}
245         * and invoking {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
246         * @param handler the resolved handler instance (never {@code null})
247         * @param request current portlet request
248         * @return the HandlerExecutionChain (never {@code null})
249         * @see #getAdaptedInterceptors()
250         */
251        protected HandlerExecutionChain getHandlerExecutionChain(Object handler, PortletRequest request) {
252                if (handler instanceof HandlerExecutionChain) {
253                        HandlerExecutionChain chain = (HandlerExecutionChain) handler;
254                        chain.addInterceptors(getAdaptedInterceptors());
255                        return chain;
256                }
257                else {
258                        return new HandlerExecutionChain(handler, getAdaptedInterceptors());
259                }
260        }
261
262}