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 13:46:46 -0400 (Fri, 02 May 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 }