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 }