    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
    018package org.apache.commons.jxpath.servlet;
    019
    020import javax.servlet.ServletContext;
    021import javax.servlet.ServletRequest;
    022import javax.servlet.http.HttpServletRequest;
    023import javax.servlet.http.HttpSession;
    024import javax.servlet.jsp.PageContext;
    025
    026import org.apache.commons.jxpath.JXPathContext;
    027import org.apache.commons.jxpath.JXPathContextFactory;
    028import org.apache.commons.jxpath.JXPathIntrospector;
    029
    030/**
    031 * Static methods that allocate and cache JXPathContexts bound to {@link PageContext}, {@link ServletRequest}, {@link HttpSession} and {@link ServletContext}.
    032 * <p>
    033 * The {@link JXPathContext} returned by {@link #getPageContext getPageContext()} provides access to all scopes via the PageContext.findAttribute() method.
    034 * Thus, an expression like "foo" will first look for the attribute named "foo" in the "page" context, then the "request" context, then the "session" one and
    035 * finally in the "application" context.
    036 * <p>
    037 * If you need to limit the attibute lookup to just one scope, you can use the pre-definded variables "page", "request", "session" and "application". For
    038 * example, the expression "$session/foo" extracts the value of the session attribute named "foo".
    039 * <p>
    040 * Following are some implementation details. There is a separate JXPathContext for each of the four scopes. These contexts are chained according to the nesting
    041 * of the scopes. So, the parent of the "page" JXPathContext is a "request" JXPathContext, whose parent is a "session" JXPathContext (that is if there is a
    042 * session), whose parent is an "application" context.
    043 * <p>
    044 * The XPath context node for each context is the corresponding object: PageContext, ServletRequest, HttpSession or ServletContext. This feature can be used by
    045 * servlets. A servlet can use one of the methods declared by this class and work with a specific JXPathContext for any scope.
    046 * <p>
    047 * Since JXPath chains lookups for variables and extension functions, variables and extension function declared in the outer scopes are also available in the
    048 * inner scopes.
    049 * <p>
    050 * Each of the four context declares exactly one variable, the value of which is the corresponding object: PageContext, etc.
    051 * <p>
    052 * The "session" variable will be undefined if there is no session for this servlet. JXPath does not automatically create sessions.
    053 */
    054public final class JXPathServletContexts {
    055
    056    private static JXPathContextFactory factory;
    057    static {
    058        JXPathIntrospector.registerDynamicClass(PageScopeContext.class, PageScopeContextHandler.class);
    059        JXPathIntrospector.registerDynamicClass(PageContext.class, PageContextHandler.class);
    060        JXPathIntrospector.registerDynamicClass(ServletContext.class, ServletContextHandler.class);
    061        JXPathIntrospector.registerDynamicClass(ServletRequestAndContext.class, ServletRequestHandler.class);
    062        JXPathIntrospector.registerDynamicClass(HttpSessionAndServletContext.class, HttpSessionHandler.class);
    063        factory = JXPathContextFactory.newInstance();
    064    }
    065
    066    /**
    067     * Returns a JXPathContext bound to the "application" scope. Caches that context within the servlet context itself.
    068     *
    069     * @param servletContext operative
    070     * @return JXPathContext
    071     */
    072    public static JXPathContext getApplicationContext(final ServletContext servletContext) {
    073        JXPathContext context = (JXPathContext) servletContext.getAttribute(Constants.JXPATH_CONTEXT);
    074        if (context == null) {
    075            context = factory.newContext(null, servletContext);
    076            context.setVariables(new KeywordVariables(Constants.APPLICATION_SCOPE, servletContext));
    077            servletContext.setAttribute(Constants.JXPATH_CONTEXT, context);
    078        }
    079        return context;
    080    }
    081
    082    /**
    083     * Returns a JXPathContext bound to the "page" scope. Caches that context within the PageContext itself.
    084     *
    085     * @param pageContext as described
    086     * @return JXPathContext
    087     */
    088    public static JXPathContext getPageContext(final PageContext pageContext) {
    089        JXPathContext context = (JXPathContext) pageContext.getAttribute(Constants.JXPATH_CONTEXT);
    090        if (context == null) {
    091            final JXPathContext parentContext = getRequestContext(pageContext.getRequest(), pageContext.getServletContext());
    092            context = factory.newContext(parentContext, pageContext);
    093            context.setVariables(new KeywordVariables(Constants.PAGE_SCOPE, new PageScopeContext(pageContext)));
    094            pageContext.setAttribute(Constants.JXPATH_CONTEXT, context);
    095        }
    096        return context;
    097    }
    098
    099    /**
    100     * Returns a JXPathContext bound to the "request" scope. Caches that context within the request itself.
    101     *
    102     * @param request        as described
    103     * @param servletContext operative
    104     * @return JXPathContext
    105     */
    106    public static JXPathContext getRequestContext(final ServletRequest request, final ServletContext servletContext) {
    107        JXPathContext context = (JXPathContext) request.getAttribute(Constants.JXPATH_CONTEXT);
    108        // If we are in an included JSP or Servlet, the request parameter
    109        // will represent the included URL, but the JXPathContext we have
    110        // just acquired will represent the outer request.
    111        if (context != null) {
    112            final ServletRequestAndContext handle = (ServletRequestAndContext) context.getContextBean();
    113            if (handle.getServletRequest() == request) {
    114                return context;
    115            }
    116        }
    117        JXPathContext parentContext = null;
    118        if (request instanceof HttpServletRequest) {
    119            final HttpSession session = ((HttpServletRequest) request).getSession(false);
    120            if (session != null) {
    121                parentContext = getSessionContext(session, servletContext);
    122            } else {
    123                parentContext = getApplicationContext(servletContext);
    124            }
    125        }
    126        final ServletRequestAndContext handle = new ServletRequestAndContext(request, servletContext);
    127        context = factory.newContext(parentContext, handle);
    128        context.setVariables(new KeywordVariables(Constants.REQUEST_SCOPE, handle));
    129        request.setAttribute(Constants.JXPATH_CONTEXT, context);
    130        return context;
    131    }
    132
    133    /**
    134     * Returns a JXPathContext bound to the "session" scope. Caches that context within the session itself.
    135     *
    136     * @param session        as described
    137     * @param servletContext operative
    138     * @return JXPathContext
    139     */
    140    public static JXPathContext getSessionContext(final HttpSession session, final ServletContext servletContext) {
    141        JXPathContext context = (JXPathContext) session.getAttribute(Constants.JXPATH_CONTEXT);
    142        if (context == null) {
    143            final JXPathContext parentContext = getApplicationContext(servletContext);
    144            final HttpSessionAndServletContext handle = new HttpSessionAndServletContext(session, servletContext);
    145            context = factory.newContext(parentContext, handle);
    146            context.setVariables(new KeywordVariables(Constants.SESSION_SCOPE, handle));
    147            session.setAttribute(Constants.JXPATH_CONTEXT, context);
    148        }
    149        return context;
    150    }
    151
    152    /**
    153     * Constructs a new instance.
    154     *
    155     * @deprecated Will be private in the next major version.
    156     */
    157    @Deprecated
    158    public JXPathServletContexts() {
    159        // empty
    160    }
    161}