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 }