001/*
002 * Copyright 2002-2015 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.Set;
020import javax.portlet.MimeResponse;
021import javax.portlet.PortletRequest;
022import javax.portlet.RenderRequest;
023import javax.portlet.RenderResponse;
024import javax.portlet.ResourceRequest;
025import javax.portlet.ResourceResponse;
026import javax.portlet.WindowState;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030
031import org.springframework.core.Ordered;
032import org.springframework.web.portlet.HandlerExceptionResolver;
033import org.springframework.web.portlet.ModelAndView;
034
035/**
036 * Abstract base class for {@link HandlerExceptionResolver} implementations.
037 *
038 * <p>Provides a set of mapped handlers that the resolver should map to,
039 * and the {@link Ordered} implementation.
040 *
041 * @author Arjen Poutsma
042 * @author Juergen Hoeller
043 * @since 3.0
044 */
045public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
046
047        /** Logger available to subclasses */
048        protected final Log logger = LogFactory.getLog(getClass());
049
050        private int order = Ordered.LOWEST_PRECEDENCE;
051
052        private Set<?> mappedHandlers;
053
054        private Class<?>[] mappedHandlerClasses;
055
056        private Log warnLogger;
057
058        private boolean renderWhenMinimized = false;
059
060
061        public void setOrder(int order) {
062                this.order = order;
063        }
064
065        @Override
066        public int getOrder() {
067                return this.order;
068        }
069
070        /**
071         * Specify the set of handlers that this exception resolver should apply to.
072         * <p>The exception mappings and the default error view will only apply to the specified handlers.
073         * <p>If no handlers or handler classes are set, the exception mappings and the default error
074         * view will apply to all handlers. This means that a specified default error view will be used
075         * as a fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be
076         * ignored in this case.
077         */
078        public void setMappedHandlers(Set<?> mappedHandlers) {
079                this.mappedHandlers = mappedHandlers;
080        }
081
082        /**
083         * Specify the set of classes that this exception resolver should apply to.
084         * <p>The exception mappings and the default error view will only apply to handlers of the
085         * specified types; the specified types may be interfaces or superclasses of handlers as well.
086         * <p>If no handlers or handler classes are set, the exception mappings and the default error
087         * view will apply to all handlers. This means that a specified default error view will be used
088         * as a fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be
089         * ignored in this case.
090         */
091        public void setMappedHandlerClasses(Class<?>... mappedHandlerClasses) {
092                this.mappedHandlerClasses = mappedHandlerClasses;
093        }
094
095        /**
096         * Set the log category for warn logging. The name will be passed to the underlying logger
097         * implementation through Commons Logging, getting interpreted as a log category according
098         * to the logger's configuration.
099         * <p>Default is no warn logging. Specify this setting to activate warn logging into a specific
100         * category. Alternatively, override the {@link #logException} method for custom logging.
101         * @see org.apache.commons.logging.LogFactory#getLog(String)
102         * @see org.apache.log4j.Logger#getLogger(String)
103         * @see java.util.logging.Logger#getLogger(String)
104         */
105        public void setWarnLogCategory(String loggerName) {
106                this.warnLogger = LogFactory.getLog(loggerName);
107        }
108
109        /**
110         * Set if the resolver should render a view when the portlet is in
111         * a minimized window. The default is "false".
112         * @see javax.portlet.RenderRequest#getWindowState()
113         * @see javax.portlet.WindowState#MINIMIZED
114         */
115        public void setRenderWhenMinimized(boolean renderWhenMinimized) {
116                this.renderWhenMinimized = renderWhenMinimized;
117        }
118
119
120        /**
121         * Checks whether this resolver is supposed to apply (i.e. the handler
122         * matches in case of "mappedHandlers" having been specified), then
123         * delegates to the {@link #doResolveException} template method.
124         */
125        @Override
126        public ModelAndView resolveException(RenderRequest request, RenderResponse response, Object handler, Exception ex) {
127                if (shouldApplyTo(request, handler)) {
128                        return doResolveException(request, response, handler, ex);
129                }
130                else {
131                        return null;
132                }
133        }
134
135        @Override
136        public ModelAndView resolveException(ResourceRequest request, ResourceResponse response, Object handler, Exception ex) {
137                if (shouldApplyTo(request, handler)) {
138                        return doResolveException(request, response, handler, ex);
139                }
140                else {
141                        return null;
142                }
143        }
144
145        /**
146         * Check whether this resolver is supposed to apply to the given handler.
147         * <p>The default implementation checks against the specified mapped handlers
148         * and handler classes, if any, and also checks the window state (according
149         * to the "renderWhenMinimize" property).
150         * @param request current portlet request
151         * @param handler the executed handler, or {@code null} if none chosen at the
152         * time of the exception (for example, if multipart resolution failed)
153         * @return whether this resolved should proceed with resolving the exception
154         * for the given request and handler
155         * @see #setMappedHandlers
156         * @see #setMappedHandlerClasses
157         */
158        protected boolean shouldApplyTo(PortletRequest request, Object handler) {
159                // If the portlet is minimized and we don't want to render then return null.
160                if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) {
161                        return false;
162                }
163                // Check mapped handlers...
164                if (handler != null) {
165                        if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
166                                return true;
167                        }
168                        if (this.mappedHandlerClasses != null) {
169                                for (Class<?> mappedClass : this.mappedHandlerClasses) {
170                                        if (mappedClass.isInstance(handler)) {
171                                                return true;
172                                        }
173                                }
174                        }
175                }
176                // Else only apply if there are no explicit handler mappings.
177                return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
178        }
179
180        /**
181         * Log the given exception at warn level, provided that warn logging has been
182         * activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
183         * <p>Calls {@link #buildLogMessage} in order to determine the concrete message to log.
184         * @param ex the exception that got thrown during handler execution
185         * @param request current portlet request (useful for obtaining metadata)
186         * @see #setWarnLogCategory
187         * @see #buildLogMessage
188         * @see org.apache.commons.logging.Log#warn(Object, Throwable)
189         */
190        protected void logException(Exception ex, PortletRequest request) {
191                if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
192                        this.warnLogger.warn(buildLogMessage(ex, request));
193                }
194        }
195
196        /**
197         * Build a log message for the given exception, occurred during processing the given request.
198         * @param ex the exception that got thrown during handler execution
199         * @param request current portlet request (useful for obtaining metadata)
200         * @return the log message to use
201         */
202        protected String buildLogMessage(Exception ex, PortletRequest request) {
203                return "Handler execution resulted in exception: " + ex;
204        }
205
206
207        /**
208         * Actually resolve the given exception that got thrown during on handler execution,
209         * returning a ModelAndView that represents a specific error page if appropriate.
210         * <p>Must be overridden in subclasses, in order to apply specific exception checks.
211         * Note that this template method will be invoked <i>after</i> checking whether this
212         * resolved applies ("mappedHandlers" etc), so an implementation may simply proceed
213         * with its actual exception handling.
214         * @param request current portlet request
215         * @param response current portlet response
216         * @param handler the executed handler, or null if none chosen at the time of
217         * the exception (for example, if multipart resolution failed)
218         * @param ex the exception that got thrown during handler execution
219         * @return a corresponding ModelAndView to forward to, or null for default processing
220         */
221        protected abstract ModelAndView doResolveException(PortletRequest request, MimeResponse response,
222                        Object handler, Exception ex);
223
224}