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    }