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 018package org.apache.commons.jxpath.servlet; 019 020import javax.servlet.ServletContext; 021import javax.servlet.ServletRequest; 022import javax.servlet.http.HttpServletRequest; 023import javax.servlet.http.HttpSession; 024import javax.servlet.jsp.PageContext; 025 026import org.apache.commons.jxpath.JXPathContext; 027import org.apache.commons.jxpath.JXPathContextFactory; 028import org.apache.commons.jxpath.JXPathIntrospector; 029 030/** 031 * Static methods that allocate and cache JXPathContexts bound to {@link PageContext}, {@link ServletRequest}, {@link HttpSession} and {@link ServletContext}. 032 * <p> 033 * The {@link JXPathContext} returned by {@link #getPageContext getPageContext()} provides access to all scopes via the PageContext.findAttribute() method. 034 * 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 035 * finally in the "application" context. 036 * <p> 037 * 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 038 * example, the expression "$session/foo" extracts the value of the session attribute named "foo". 039 * <p> 040 * Following are some implementation details. There is a separate JXPathContext for each of the four scopes. These contexts are chained according to the nesting 041 * 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 042 * session), whose parent is an "application" context. 043 * <p> 044 * The XPath context node for each context is the corresponding object: PageContext, ServletRequest, HttpSession or ServletContext. This feature can be used by 045 * servlets. A servlet can use one of the methods declared by this class and work with a specific JXPathContext for any scope. 046 * <p> 047 * Since JXPath chains lookups for variables and extension functions, variables and extension function declared in the outer scopes are also available in the 048 * inner scopes. 049 * <p> 050 * Each of the four context declares exactly one variable, the value of which is the corresponding object: PageContext, etc. 051 * <p> 052 * The "session" variable will be undefined if there is no session for this servlet. JXPath does not automatically create sessions. 053 */ 054public final class JXPathServletContexts { 055 056 private static JXPathContextFactory factory; 057 static { 058 JXPathIntrospector.registerDynamicClass(PageScopeContext.class, PageScopeContextHandler.class); 059 JXPathIntrospector.registerDynamicClass(PageContext.class, PageContextHandler.class); 060 JXPathIntrospector.registerDynamicClass(ServletContext.class, ServletContextHandler.class); 061 JXPathIntrospector.registerDynamicClass(ServletRequestAndContext.class, ServletRequestHandler.class); 062 JXPathIntrospector.registerDynamicClass(HttpSessionAndServletContext.class, HttpSessionHandler.class); 063 factory = JXPathContextFactory.newInstance(); 064 } 065 066 /** 067 * Returns a JXPathContext bound to the "application" scope. Caches that context within the servlet context itself. 068 * 069 * @param servletContext operative 070 * @return JXPathContext 071 */ 072 public static JXPathContext getApplicationContext(final ServletContext servletContext) { 073 JXPathContext context = (JXPathContext) servletContext.getAttribute(Constants.JXPATH_CONTEXT); 074 if (context == null) { 075 context = factory.newContext(null, servletContext); 076 context.setVariables(new KeywordVariables(Constants.APPLICATION_SCOPE, servletContext)); 077 servletContext.setAttribute(Constants.JXPATH_CONTEXT, context); 078 } 079 return context; 080 } 081 082 /** 083 * Returns a JXPathContext bound to the "page" scope. Caches that context within the PageContext itself. 084 * 085 * @param pageContext as described 086 * @return JXPathContext 087 */ 088 public static JXPathContext getPageContext(final PageContext pageContext) { 089 JXPathContext context = (JXPathContext) pageContext.getAttribute(Constants.JXPATH_CONTEXT); 090 if (context == null) { 091 final JXPathContext parentContext = getRequestContext(pageContext.getRequest(), pageContext.getServletContext()); 092 context = factory.newContext(parentContext, pageContext); 093 context.setVariables(new KeywordVariables(Constants.PAGE_SCOPE, new PageScopeContext(pageContext))); 094 pageContext.setAttribute(Constants.JXPATH_CONTEXT, context); 095 } 096 return context; 097 } 098 099 /** 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}