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 */ 017package org.apache.commons.jxpath.servlet; 018 019import javax.servlet.ServletContext; 020import javax.servlet.ServletRequest; 021import javax.servlet.http.HttpServletRequest; 022import javax.servlet.http.HttpSession; 023import javax.servlet.jsp.PageContext; 024import org.apache.commons.jxpath.JXPathContext; 025import org.apache.commons.jxpath.JXPathContextFactory; 026import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $ 067 */ 068public 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}