View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jxpath.servlet;
18  
19  import javax.servlet.ServletContext;
20  import javax.servlet.ServletRequest;
21  import javax.servlet.http.HttpServletRequest;
22  import javax.servlet.http.HttpSession;
23  import javax.servlet.jsp.PageContext;
24  import org.apache.commons.jxpath.JXPathContext;
25  import org.apache.commons.jxpath.JXPathContextFactory;
26  import org.apache.commons.jxpath.JXPathIntrospector;
27  
28  /**
29   * Static methods that allocate and cache JXPathContexts bound to
30   * {@link PageContext}, {@link ServletRequest}, {@link HttpSession}
31   * and {@link ServletContext}.
32   * <p>
33   * The {@link JXPathContext} returned by {@link #getPageContext getPageContext()}
34   * provides access to all scopes via the PageContext.findAttribute()
35   * method.  Thus, an expression like "foo" will first look for the attribute
36   * named "foo" in the "page" context, then the "request" context, then
37   * the "session" one and finally in the "application" context.
38   * <p>
39   * If you need to limit the attibute lookup to just one scope, you can use the
40   * pre-definded variables "page", "request", "session" and "application".
41   * For example, the expression "$session/foo" extracts the value of the
42   * session attribute named "foo".
43   * <p>
44   * Following are some implementation details. There is a separate JXPathContext
45   * for each of the four scopes. These contexts are chained according to the
46   * nesting of the scopes.  So, the parent of the "page" JXPathContext is a
47   * "request" JXPathContext, whose parent is a "session" JXPathContext (that is
48   * if there is a session), whose parent is an "application" context.
49   * <p>
50   * The  XPath context node for each context is the corresponding object:
51   * PageContext, ServletRequest, HttpSession or ServletContext.  This feature can
52   * be used by servlets.  A servlet can use one of the methods declared by this
53   * class and work with a specific JXPathContext for any scope.
54   * <p>
55   * Since JXPath chains lookups for variables and extension functions, variables
56   * and extension function declared in the outer scopes are also available in
57   * the inner scopes.
58   * <p>
59   * Each  of the four context declares exactly one variable, the value of which
60   * is the corresponding object: PageContext, etc.
61   * <p>
62   * The  "session" variable will be undefined if there is no session for this
63   * servlet. JXPath does not automatically create sessions.
64   *
65   * @author Dmitri Plotnikov
66   * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
67   */
68  public final class JXPathServletContexts {
69  
70      private static JXPathContextFactory factory;
71  
72      static {
73          JXPathIntrospector.registerDynamicClass(
74                  PageScopeContext.class,
75                  PageScopeContextHandler.class);
76          JXPathIntrospector.registerDynamicClass(
77                  PageContext.class,
78                  PageContextHandler.class);
79          JXPathIntrospector.registerDynamicClass(
80                  ServletContext.class,
81                  ServletContextHandler.class);
82          JXPathIntrospector.registerDynamicClass(
83                  ServletRequestAndContext.class,
84                  ServletRequestHandler.class);
85          JXPathIntrospector.registerDynamicClass(
86                  HttpSessionAndServletContext.class,
87                  HttpSessionHandler.class);
88          factory = JXPathContextFactory.newInstance();
89      }
90  
91      /**
92       * Returns a JXPathContext bound to the "page" scope. Caches that context
93       * within the PageContext itself.
94       * @param pageContext as described
95       * @return JXPathContext
96       */
97      public static JXPathContext getPageContext(PageContext pageContext) {
98          JXPathContext context =
99              (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 }