001/*
002 * Copyright 2002-2018 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.servlet.tags;
018
019import javax.servlet.jsp.JspException;
020import javax.servlet.jsp.JspTagException;
021import javax.servlet.jsp.tagext.TagSupport;
022import javax.servlet.jsp.tagext.TryCatchFinally;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027import org.springframework.lang.Nullable;
028import org.springframework.util.Assert;
029import org.springframework.web.servlet.support.JspAwareRequestContext;
030import org.springframework.web.servlet.support.RequestContext;
031
032/**
033 * Superclass for all tags that require a {@link RequestContext}.
034 *
035 * <p>The {@code RequestContext} instance provides easy access
036 * to current state like the
037 * {@link org.springframework.web.context.WebApplicationContext},
038 * the {@link java.util.Locale}, the
039 * {@link org.springframework.ui.context.Theme}, etc.
040 *
041 * <p>Mainly intended for
042 * {@link org.springframework.web.servlet.DispatcherServlet} requests;
043 * will use fallbacks when used outside {@code DispatcherServlet}.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @see org.springframework.web.servlet.support.RequestContext
048 * @see org.springframework.web.servlet.DispatcherServlet
049 */
050@SuppressWarnings("serial")
051public abstract class RequestContextAwareTag extends TagSupport implements TryCatchFinally {
052
053        /**
054         * {@link javax.servlet.jsp.PageContext} attribute for the
055         * page-level {@link RequestContext} instance.
056         */
057        public static final String REQUEST_CONTEXT_PAGE_ATTRIBUTE =
058                        "org.springframework.web.servlet.tags.REQUEST_CONTEXT";
059
060
061        /** Logger available to subclasses. */
062        protected final Log logger = LogFactory.getLog(getClass());
063
064
065        @Nullable
066        private RequestContext requestContext;
067
068
069        /**
070         * Create and expose the current RequestContext.
071         * Delegates to {@link #doStartTagInternal()} for actual work.
072         * @see #REQUEST_CONTEXT_PAGE_ATTRIBUTE
073         * @see org.springframework.web.servlet.support.JspAwareRequestContext
074         */
075        @Override
076        public final int doStartTag() throws JspException {
077                try {
078                        this.requestContext = (RequestContext) this.pageContext.getAttribute(REQUEST_CONTEXT_PAGE_ATTRIBUTE);
079                        if (this.requestContext == null) {
080                                this.requestContext = new JspAwareRequestContext(this.pageContext);
081                                this.pageContext.setAttribute(REQUEST_CONTEXT_PAGE_ATTRIBUTE, this.requestContext);
082                        }
083                        return doStartTagInternal();
084                }
085                catch (JspException | RuntimeException ex) {
086                        logger.error(ex.getMessage(), ex);
087                        throw ex;
088                }
089                catch (Exception ex) {
090                        logger.error(ex.getMessage(), ex);
091                        throw new JspTagException(ex.getMessage());
092                }
093        }
094
095        /**
096         * Return the current RequestContext.
097         */
098        protected final RequestContext getRequestContext() {
099                Assert.state(this.requestContext != null, "No current RequestContext");
100                return this.requestContext;
101        }
102
103        /**
104         * Called by doStartTag to perform the actual work.
105         * @return same as TagSupport.doStartTag
106         * @throws Exception any exception, any checked one other than
107         * a JspException gets wrapped in a JspException by doStartTag
108         * @see javax.servlet.jsp.tagext.TagSupport#doStartTag
109         */
110        protected abstract int doStartTagInternal() throws Exception;
111
112
113        @Override
114        public void doCatch(Throwable throwable) throws Throwable {
115                throw throwable;
116        }
117
118        @Override
119        public void doFinally() {
120                this.requestContext = null;
121        }
122
123}