View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.common.xsl;
20  
21  import java.util.Collections;
22  import java.util.Enumeration;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Objects;
26  
27  import javax.xml.transform.Transformer;
28  
29  import org.apache.logging.log4j.LogManager;
30  import org.apache.logging.log4j.Logger;
31  import org.mycore.common.MCRConstants;
32  import org.mycore.common.MCRSession;
33  import org.mycore.common.MCRSessionMgr;
34  import org.mycore.common.config.MCRConfiguration2;
35  import org.mycore.frontend.MCRFrontendUtil;
36  import org.mycore.frontend.servlets.MCRServlet;
37  import org.mycore.frontend.servlets.MCRServletJob;
38  
39  import jakarta.servlet.http.HttpServletRequest;
40  import jakarta.servlet.http.HttpSession;
41  
42  /**
43   * Collects parameters used in XSL transformations, by copying them from
44   * MCRConfiguration, from the HTTP and MyCoRe session, from request attributes etc. 
45   * 
46   * @author Frank L\u00FCtzenkirchen
47   */
48  public class MCRParameterCollector {
49  
50      private static final Logger LOGGER = LogManager.getLogger(MCRParameterCollector.class);
51  
52      /** The collected parameters */
53      private Map<String, Object> parameters = new HashMap<>();
54  
55      /** If true (which is default), only those parameters starting with "XSL." are copied from session and request */
56      private boolean onlySetXSLParameters = true;
57  
58      private boolean modified = true;
59  
60      private int hashCode;
61  
62      /**
63       * Collects parameters The collecting of parameters is done in steps,
64       * each step may overwrite parameters that already have been set.
65       * 
66       * First, all configuration properties from MCRConfiguration are copied.
67       * Second, those variables stored in the HTTP session, that start with "XSL." are copied.
68       * Next, variables stored in the MCRSession are copied.
69       * Next, HTTP request parameters are copied.
70       * 
71       * Only those parameters starting with "XSL." are copied from session and request,
72       * 
73       * @param request the HttpRequest causing the XSL transformation, must NOT be null
74       */
75      public MCRParameterCollector(HttpServletRequest request) {
76          this(request, true);
77      }
78  
79      /**
80       * Collects parameters The collecting of parameters is done in steps,
81       * each step may overwrite parameters that already have been set.
82       * 
83       * First, all configuration properties from MCRConfiguration are copied.
84       * Second, those variables stored in the HTTP session, that start with "XSL." are copied.
85       * Next, variables stored in the MCRSession are copied.
86       * Next, HTTP request parameters are copied.
87       * Next, HTTP request attributes are copied.
88       * 
89       * @param request the HttpRequest causing the XSL transformation, must NOT be null
90       * @param onlySetXSLParameters if true, only those parameters starting with "XSL."
91       *                            are copied from session and request
92       */
93      public MCRParameterCollector(HttpServletRequest request, boolean onlySetXSLParameters) {
94          this.onlySetXSLParameters = onlySetXSLParameters;
95  
96          setFromConfiguration();
97  
98          HttpSession session = request.getSession(false);
99          if (session != null) {
100             setFromSession(session);
101         }
102 
103         if (!MCRSessionMgr.isLocked()) {
104             MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
105             setFromSession(mcrSession);
106             setUnmodifyableParameters(mcrSession, request);
107         }
108         setFromRequestParameters(request);
109         setFromRequestAttributes(request);
110         setFromRequestHeader(request);
111 
112         if (session != null) {
113             setSessionID(session, request.isRequestedSessionIdFromCookie());
114         }
115 
116         debugSessionParameters();
117     }
118 
119     /**
120      * Collects parameters The collecting of parameters is done in steps,
121      * each step may overwrite parameters that already have been set.
122      * 
123      * First, all configuration properties from MCRConfiguration are copied.
124      * Next, those variables stored in the MCRSession that start with "XSL." are copied.
125      */
126     public MCRParameterCollector() {
127         this(true);
128     }
129 
130     /**
131      * Collects parameters The collecting of parameters is done in steps,
132      * each step may overwrite parameters that already have been set.
133      * 
134      * First, all configuration properties from MCRConfiguration are copied.
135      * Next, those variables stored in the MCRSession are copied.
136      * 
137      * @param onlySetXSLParameters if true, only those parameters starting with "XSL." are copied from session
138      */
139     public MCRParameterCollector(boolean onlySetXSLParameters) {
140         this.onlySetXSLParameters = onlySetXSLParameters;
141         setFromConfiguration();
142         MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
143         setFromSession(mcrSession);
144         setUnmodifyableParameters(mcrSession, null);
145         debugSessionParameters();
146     }
147 
148     /**
149      * Sets the parameter with the given name
150      */
151     public void setParameter(String name, Object value) {
152         parameters.put(name, value);
153         modified = true;
154     }
155 
156     /**
157      * Sets all parameters from the given map
158      */
159     public void setParameters(Map<String, String> param) {
160         parameters.putAll(param);
161         modified = true;
162     }
163 
164     /**
165      * Sets the parameter only if it is not empty and starts with "XSL." or onlySetXSLParameters is false
166      */
167     private void setXSLParameter(String name, String value) {
168         if ((value == null) || value.isEmpty()) {
169             return;
170         }
171         if (name.startsWith("XSL.")) {
172             parameters.put(name.substring(4), value);
173         } else if (!onlySetXSLParameters) {
174             parameters.put(name, value);
175         }
176     }
177 
178     /**
179      * Returns the parameter with the given name
180      */
181     public String getParameter(String name, String defaultValue) {
182         Object val = parameters.get(name);
183         return (val == null) ? defaultValue : val.toString();
184     }
185 
186     /**
187      * Returns the parameter map.  
188      */
189     public Map<String, Object> getParameterMap() {
190         return Collections.unmodifiableMap(parameters);
191     }
192 
193     /**
194      * Copies all MCRConfiguration properties as XSL parameters.
195      * and replaces ":" in property keys with "_"
196      */
197     private void setFromConfiguration() {
198         for (Map.Entry<String, String> property : MCRConfiguration2.getPropertiesMap().entrySet()) {
199             String key = property.getKey().replace(":", "_");
200             parameters.put(key, property.getValue());
201         }
202     }
203 
204     /**
205      * Sets those session variables as XSL parameters that start with "XSL.",
206      * others will be ignored. The "XSL." prefix is cut off from the name.
207      */
208     private void setFromSession(HttpSession session) {
209         for (Enumeration<String> e = session.getAttributeNames(); e.hasMoreElements();) {
210             String name = e.nextElement();
211             setXSLParameter(name, session.getAttribute(name).toString());
212         }
213     }
214 
215     /**
216      * Sets those session variables as XSL parameters that start with "XSL.",
217      * others will be ignored. The "XSL." prefix is cut off from the name.
218      */
219     private void setFromSession(MCRSession session) {
220         Objects.requireNonNull(session, "Session needs to be not null!");
221         for (Map.Entry<Object, Object> entry : session.getMapEntries()) {
222             String key = entry.getKey().toString();
223             if (entry.getValue() != null) {
224                 setXSLParameter(key, entry.getValue().toString());
225             }
226         }
227     }
228 
229     /**
230      * Sets those request attributes as XSL parameters that start with "XSL.",
231      * others will be ignored. The "XSL." prefix is cut off from the name.
232      */
233     private void setFromRequestParameters(HttpServletRequest request) {
234         for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
235             String name = e.nextElement();
236             if (!(name.endsWith(".SESSION"))) {
237                 setXSLParameter(name, request.getParameter(name));
238             }
239         }
240     }
241 
242     /**
243      * Sets those request parameters as XSL parameters that start with "XSL.",
244      * others will be ignored. The "XSL." prefix is cut off from the name.
245      */
246     private void setFromRequestAttributes(HttpServletRequest request) {
247         for (Enumeration<String> e = request.getAttributeNames(); e.hasMoreElements();) {
248             String name = e.nextElement();
249             if (!(name.endsWith(".SESSION"))) {
250                 final Object attributeValue = request.getAttribute(name);
251                 if (attributeValue != null) {
252                     setXSLParameter(name, attributeValue.toString());
253                 }
254             }
255         }
256     }
257 
258     /**
259      * Sets the ID of the current session as parameter
260      */
261     private void setSessionID(HttpSession session, boolean isFromCookie) {
262         String sessionParam = MCRConfiguration2.getString("MCR.Session.Param").orElse(";jsessionid=");
263         String jSessionID = sessionParam + session.getId();
264         parameters.put("JSessionID", jSessionID);
265         if (!isFromCookie) {
266             parameters.put("HttpSession", jSessionID);
267         }
268     }
269 
270     /**
271      * Sets some parameters that must not be overwritten by the request, for example
272      * the user ID and the URL of the web application.
273      * 
274      * @param session
275      * @param request 
276      */
277     private void setUnmodifyableParameters(MCRSession session, HttpServletRequest request) {
278         parameters.put("CurrentUser", session.getUserInformation().getUserID());
279         parameters.put("CurrentLang", session.getCurrentLanguage());
280         parameters.put("WebApplicationBaseURL", MCRFrontendUtil.getBaseURL());
281         parameters.put("ServletsBaseURL", MCRServlet.getServletBaseURL());
282         String defaultLang = MCRConfiguration2.getString("MCR.Metadata.DefaultLang").orElse(MCRConstants.DEFAULT_LANG);
283         parameters.put("DefaultLang", defaultLang);
284 
285         String userAgent = request != null ? request.getHeader("User-Agent") : null;
286         if (userAgent != null) {
287             parameters.put("User-Agent", userAgent);
288         }
289 
290     }
291 
292     private void debugSessionParameters() {
293         LOGGER.debug("XSL.HttpSession ={}", parameters.get("HttpSession"));
294         LOGGER.debug("XSL.JSessionID ={}", parameters.get("JSessionID"));
295         LOGGER.debug("XSL.CurrentUser ={}", parameters.get("CurrentUser"));
296         LOGGER.debug("XSL.Referer ={}", parameters.get("Referer"));
297     }
298 
299     /** Sets the request and referer URL */
300     private void setFromRequestHeader(HttpServletRequest request) {
301         parameters.put("RequestURL", getCompleteURL(request));
302         parameters.put("Referer", request.getHeader("Referer") != null ? request.getHeader("Referer") : "");
303         parameters.put("UserAgent", request.getHeader("User-Agent") != null ? request.getHeader("User-Agent") : "");
304     }
305 
306     /** 
307      * Calculates the complete request URL, so that mod_proxy is supported 
308      */
309     private String getCompleteURL(HttpServletRequest request) {
310         StringBuilder buffer = getBaseURLUpToHostName();
311 
312         //when called by MCRErrorServlet
313         String errorURI = (String) request.getAttribute("jakarta.servlet.error.request_uri");
314         buffer.append(errorURI != null ? errorURI : request.getRequestURI());
315 
316         String queryString = request.getQueryString();
317         if (queryString != null && queryString.length() > 0) {
318             buffer.append("?").append(queryString);
319         }
320 
321         String url = buffer.toString();
322         LOGGER.debug("Complete request URL : {}", url);
323         return url;
324     }
325 
326     private StringBuilder getBaseURLUpToHostName() {
327         int schemeLength = "https://".length();
328         String baseURL = MCRFrontendUtil.getBaseURL();
329         StringBuilder buffer = new StringBuilder(baseURL);
330         if (baseURL.length() < schemeLength) {
331             return buffer;
332         }
333         int pos = buffer.indexOf("/", schemeLength);
334         buffer.delete(pos, buffer.length());
335         return buffer;
336     }
337 
338     /**
339      * Sets XSL parameters for the given transformer by taking them from the
340      * properties object provided.
341      * 
342      * @param transformer
343      *            the Transformer object thats parameters should be set
344      */
345     public void setParametersTo(Transformer transformer) {
346         for (Map.Entry<String, Object> entry : parameters.entrySet()) {
347             transformer.setParameter(entry.getKey(), entry.getValue());
348         }
349     }
350 
351     public static MCRParameterCollector getInstanceFromUserSession() {
352         MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
353         MCRServletJob job = (MCRServletJob) mcrSession.get("MCRServletJob");
354         return job == null ? new MCRParameterCollector() : new MCRParameterCollector(job.getRequest());
355     }
356 
357     public int hashCode() {
358         if (modified) {
359             int result = LOGGER.hashCode();
360             //order of map should not harm result
361             result += parameters.entrySet().stream().mapToInt(Map.Entry::hashCode).sum();
362             hashCode = result;
363             modified = false;
364         }
365         return hashCode;
366     }
367 }