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  
18  package org.apache.commons.jxpath.servlet;
19  
20  import javax.servlet.ServletContext;
21  import javax.servlet.ServletRequest;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpSession;
24  import javax.servlet.jsp.PageContext;
25  
26  import org.apache.commons.jxpath.JXPathContext;
27  import org.apache.commons.jxpath.JXPathContextFactory;
28  import org.apache.commons.jxpath.JXPathIntrospector;
29  
30  /**
31   * Static methods that allocate and cache JXPathContexts bound to {@link PageContext}, {@link ServletRequest}, {@link HttpSession} and {@link ServletContext}.
32   * <p>
33   * The {@link JXPathContext} returned by {@link #getPageContext getPageContext()} provides access to all scopes via the PageContext.findAttribute() method.
34   * 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
35   * finally in the "application" context.
36   * <p>
37   * 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
38   * example, the expression "$session/foo" extracts the value of the session attribute named "foo".
39   * <p>
40   * Following are some implementation details. There is a separate JXPathContext for each of the four scopes. These contexts are chained according to the nesting
41   * 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
42   * session), whose parent is an "application" context.
43   * <p>
44   * The XPath context node for each context is the corresponding object: PageContext, ServletRequest, HttpSession or ServletContext. This feature can be used by
45   * servlets. A servlet can use one of the methods declared by this class and work with a specific JXPathContext for any scope.
46   * <p>
47   * Since JXPath chains lookups for variables and extension functions, variables and extension function declared in the outer scopes are also available in the
48   * inner scopes.
49   * <p>
50   * Each of the four context declares exactly one variable, the value of which is the corresponding object: PageContext, etc.
51   * <p>
52   * The "session" variable will be undefined if there is no session for this servlet. JXPath does not automatically create sessions.
53   */
54  public final class JXPathServletContexts {
55  
56      private static JXPathContextFactory factory;
57      static {
58          JXPathIntrospector.registerDynamicClass(PageScopeContext.class, PageScopeContextHandler.class);
59          JXPathIntrospector.registerDynamicClass(PageContext.class, PageContextHandler.class);
60          JXPathIntrospector.registerDynamicClass(ServletContext.class, ServletContextHandler.class);
61          JXPathIntrospector.registerDynamicClass(ServletRequestAndContext.class, ServletRequestHandler.class);
62          JXPathIntrospector.registerDynamicClass(HttpSessionAndServletContext.class, HttpSessionHandler.class);
63          factory = JXPathContextFactory.newInstance();
64      }
65  
66      /**
67       * Returns a JXPathContext bound to the "application" scope. Caches that context within the servlet context itself.
68       *
69       * @param servletContext operative
70       * @return JXPathContext
71       */
72      public static JXPathContext getApplicationContext(final ServletContext servletContext) {
73          JXPathContext context = (JXPathContext) servletContext.getAttribute(Constants.JXPATH_CONTEXT);
74          if (context == null) {
75              context = factory.newContext(null, servletContext);
76              context.setVariables(new KeywordVariables(Constants.APPLICATION_SCOPE, servletContext));
77              servletContext.setAttribute(Constants.JXPATH_CONTEXT, context);
78          }
79          return context;
80      }
81  
82      /**
83       * Returns a JXPathContext bound to the "page" scope. Caches that context within the PageContext itself.
84       *
85       * @param pageContext as described
86       * @return JXPathContext
87       */
88      public static JXPathContext getPageContext(final PageContext pageContext) {
89          JXPathContext context = (JXPathContext) pageContext.getAttribute(Constants.JXPATH_CONTEXT);
90          if (context == null) {
91              final JXPathContext parentContext = getRequestContext(pageContext.getRequest(), pageContext.getServletContext());
92              context = factory.newContext(parentContext, pageContext);
93              context.setVariables(new KeywordVariables(Constants.PAGE_SCOPE, new PageScopeContext(pageContext)));
94              pageContext.setAttribute(Constants.JXPATH_CONTEXT, context);
95          }
96          return context;
97      }
98  
99      /**
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 }