    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.servlet;
    018
    019    import javax.servlet.ServletContext;
    020    import javax.servlet.ServletRequest;
    021    import javax.servlet.http.HttpServletRequest;
    022    import javax.servlet.http.HttpSession;
    023    import javax.servlet.jsp.PageContext;
    024    import org.apache.commons.jxpath.JXPathContext;
    025    import org.apache.commons.jxpath.JXPathContextFactory;
    026    import org.apache.commons.jxpath.JXPathIntrospector;
    027
    028    /**
    029     * Static methods that allocate and cache JXPathContexts bound to
    030     * {@link PageContext}, {@link ServletRequest}, {@link HttpSession}
    031     * and {@link ServletContext}.
    032     * <p>
    033     * The {@link JXPathContext} returned by {@link #getPageContext getPageContext()}
    034     * provides access to all scopes via the PageContext.findAttribute()
    035     * method.  Thus, an expression like "foo" will first look for the attribute
    036     * named "foo" in the "page" context, then the "request" context, then
    037     * the "session" one and finally in the "application" context.
    038     * <p>
    039     * If you need to limit the attibute lookup to just one scope, you can use the
    040     * pre-definded variables "page", "request", "session" and "application".
    041     * For example, the expression "$session/foo" extracts the value of the
    042     * session attribute named "foo".
    043     * <p>
    044     * Following are some implementation details. There is a separate JXPathContext
    045     * for each of the four scopes. These contexts are chained according to the
    046     * nesting of the scopes.  So, the parent of the "page" JXPathContext is a
    047     * "request" JXPathContext, whose parent is a "session" JXPathContext (that is
    048     * if there is a session), whose parent is an "application" context.
    049     * <p>
    050     * The  XPath context node for each context is the corresponding object:
    051     * PageContext, ServletRequest, HttpSession or ServletContext.  This feature can
    052     * be used by servlets.  A servlet can use one of the methods declared by this
    053     * class and work with a specific JXPathContext for any scope.
    054     * <p>
    055     * Since JXPath chains lookups for variables and extension functions, variables
    056     * and extension function declared in the outer scopes are also available in
    057     * the inner scopes.
    058     * <p>
    059     * Each  of the four context declares exactly one variable, the value of which
    060     * is the corresponding object: PageContext, etc.
    061     * <p>
    062     * The  "session" variable will be undefined if there is no session for this
    063     * servlet. JXPath does not automatically create sessions.
    064     *
    065     * @author Dmitri Plotnikov
    066     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    067     */
    068    public final class JXPathServletContexts {
    069
    070        private static JXPathContextFactory factory;
    071
    072        static {
    073            JXPathIntrospector.registerDynamicClass(
    074                    PageScopeContext.class,
    075                    PageScopeContextHandler.class);
    076            JXPathIntrospector.registerDynamicClass(
    077                    PageContext.class,
    078                    PageContextHandler.class);
    079            JXPathIntrospector.registerDynamicClass(
    080                    ServletContext.class,
    081                    ServletContextHandler.class);
    082            JXPathIntrospector.registerDynamicClass(
    083                    ServletRequestAndContext.class,
    084                    ServletRequestHandler.class);
    085            JXPathIntrospector.registerDynamicClass(
    086                    HttpSessionAndServletContext.class,
    087                    HttpSessionHandler.class);
    088            factory = JXPathContextFactory.newInstance();
    089        }
    090
    091        /**
    092         * Returns a JXPathContext bound to the "page" scope. Caches that context
    093         * within the PageContext itself.
    094         * @param pageContext as described
    095         * @return JXPathContext
    096         */
    097        public static JXPathContext getPageContext(PageContext pageContext) {
    098            JXPathContext context =
    099                (JXPathContext) pageContext.getAttribute(Constants.JXPATH_CONTEXT);
    100            if (context == null) {
    101                JXPathContext parentContext =
    102                    getRequestContext(
    103                        pageContext.getRequest(),
    104                        pageContext.getServletContext());
    105                context = factory.newContext(parentContext, pageContext);
    106                context.setVariables(
    107                    new KeywordVariables(
    108                        Constants.PAGE_SCOPE,
    109                        new PageScopeContext(pageContext)));
    110                pageContext.setAttribute(Constants.JXPATH_CONTEXT, context);
    111            }
    112            return context;
    113        }
    114
    115        /**
    116         * Returns a JXPathContext bound to the "request" scope. Caches that context
    117         * within the request itself.
    118         * @param request as described
    119         * @param servletContext operative
    120         * @return JXPathContext
    121         */
    122        public static JXPathContext getRequestContext(ServletRequest request,
    123                ServletContext servletContext) {
    124            JXPathContext context =
    125                (JXPathContext) request.getAttribute(Constants.JXPATH_CONTEXT);
    126            // If we are in an included JSP or Servlet, the request parameter
    127            // will represent the included URL, but the JXPathContext we have
    128            // just acquired will represent the outer request.
    129            if (context != null) {
    130                ServletRequestAndContext handle =
    131                    (ServletRequestAndContext) context.getContextBean();
    132                if (handle.getServletRequest() == request) {
    133                    return context;
    134                }
    135            }
    136
    137            JXPathContext parentContext = null;
    138            if (request instanceof HttpServletRequest) {
    139                HttpSession session =
    140                    ((HttpServletRequest) request).getSession(false);
    141                if (session != null) {
    142                    parentContext = getSessionContext(session, servletContext);
    143                }
    144                else {
    145                    parentContext = getApplicationContext(servletContext);
    146                }
    147            }
    148            ServletRequestAndContext handle =
    149                new ServletRequestAndContext(request, servletContext);
    150            context = factory.newContext(parentContext, handle);
    151            context.setVariables(
    152                new KeywordVariables(Constants.REQUEST_SCOPE, handle));
    153            request.setAttribute(Constants.JXPATH_CONTEXT, context);
    154            return context;
    155        }
    156
    157        /**
    158         * Returns a JXPathContext bound to the "session" scope. Caches that context
    159         * within the session itself.
    160         * @param session as described
    161         * @param servletContext operative
    162         * @return JXPathContext
    163         */
    164        public static JXPathContext getSessionContext(HttpSession session,
    165                ServletContext servletContext) {
    166            JXPathContext context =
    167                (JXPathContext) session.getAttribute(Constants.JXPATH_CONTEXT);
    168            if (context == null) {
    169                JXPathContext parentContext = getApplicationContext(servletContext);
    170                HttpSessionAndServletContext handle =
    171                    new HttpSessionAndServletContext(session, servletContext);
    172                context = factory.newContext(parentContext, handle);
    173                context.setVariables(
    174                    new KeywordVariables(Constants.SESSION_SCOPE, handle));
    175                session.setAttribute(Constants.JXPATH_CONTEXT, context);
    176            }
    177            return context;
    178        }
    179
    180        /**
    181         * Returns  a JXPathContext bound to the "application" scope. Caches that
    182         * context within the servlet context itself.
    183         * @param servletContext operative
    184         * @return JXPathContext
    185         */
    186        public static JXPathContext getApplicationContext(
    187                ServletContext servletContext) {
    188            JXPathContext context =
    189                (JXPathContext) servletContext.getAttribute(
    190                    Constants.JXPATH_CONTEXT);
    191            if (context == null) {
    192                context = factory.newContext(null, servletContext);
    193                context.setVariables(
    194                    new KeywordVariables(
    195                        Constants.APPLICATION_SCOPE,
    196                        servletContext));
    197                servletContext.setAttribute(Constants.JXPATH_CONTEXT, context);
    198            }
    199            return context;
    200        }
    201    }