View Javadoc
1   /**
2    * This file Copyright (c) 2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.cache.cachekey;
35  
36  import info.magnolia.cms.core.AggregationState;
37  import info.magnolia.cms.security.MgnlUserManager;
38  import info.magnolia.cms.security.User;
39  import info.magnolia.context.MgnlContext;
40  
41  import java.io.Serializable;
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.List;
45  import java.util.Locale;
46  import java.util.Map;
47  import java.util.SortedMap;
48  import java.util.TreeMap;
49  
50  import javax.servlet.http.HttpServletRequest;
51  
52  import org.apache.commons.lang3.StringUtils;
53  
54  /**
55   * Default cache key implementation. Key is based on the URI, server name, parameters and request headers. Since the server name is likely to change from server to server, copying cached items around will most likely not help to avoid generating cache entries.
56   */
57  public class DefaultCacheKeyGenerator implements CacheKeyGenerator<DefaultCacheKey> {
58  
59      private boolean useRequestParameters = true;
60      private boolean useRequestUri = true;
61      private boolean useRequestMethod = true;
62      private boolean useRequestGetSecure = true;
63      private boolean useRequestServerName = true;
64  
65      private boolean useUserName = true;
66      private boolean useLocale = true;
67      private boolean useChannel = true;
68  
69      @Override
70      public DefaultCacheKey generateKey(final AggregationState aggregationState) {
71          // get original URI - not using current URI since we want to cache original URIs, not those we forward to (parameters in virtual URIs, i18n, ...)
72          final String uri = useRequestUri ? aggregationState.getOriginalURI() : null;
73  
74          // get serverName and request params from WebContext
75          final String serverName;
76          final Map<String, String> params;
77          final boolean isSecure;
78          final String method;
79          if (MgnlContext.isWebContext()) {
80              HttpServletRequest request = MgnlContext.getWebContext().getRequest();
81              params = useRequestParameters ? getParametersFromRequest(request) : null;
82              serverName = useRequestServerName ? request.getServerName() : null;
83              isSecure = useRequestGetSecure ? request.isSecure() : false;
84              method = useRequestMethod ? request.getMethod().toLowerCase() : null;
85          } else {
86              serverName = null;
87              params = null;
88              isSecure = false;
89              method = null;
90          }
91  
92          // get locale
93          final Locale locale = useLocale ? aggregationState.getLocale() : null;
94          final String localeStr = locale == null ? null : locale.toString();
95  
96          final SortedMap<String, Serializable> additionalAttributes = new TreeMap<>();
97          final String userName = MgnlContext.getUser().getName();
98          if (useUserName && !MgnlUserManager.ANONYMOUS_USER.equals(userName)) {
99              additionalAttributes.put(User.class.getName(), userName);
100         }
101         final String channel = useChannel ? aggregationState.getChannel().getName() : null;
102 
103         return this.createKey(aggregationState, uri, serverName, localeStr, channel, method, params, isSecure, additionalAttributes);
104     }
105 
106     protected DefaultCacheKey createKey(final AggregationState aggregationState, String uri, String serverName, String locale, String channel, String method,
107                                         Map<String, String> params, Boolean isSecure, SortedMap<String, Serializable> additionalAttributes) {
108         // create composite key so we can easily check each part of it later
109         return new DefaultCacheKey(
110                 uri,
111                 serverName,
112                 locale,
113                 channel,
114                 method,
115                 params == null ? null : Collections.unmodifiableMap(params),
116                 isSecure,
117                 additionalAttributes == null ? null : Collections.unmodifiableMap(additionalAttributes)
118         );
119     }
120 
121     /**
122      * Returns sorted map (by keys) with parameters and values (sorted as well). If request holds more values for one parameter
123      * then values are separated by semicolon. Sorted keys and values ensure we will not cache same page if order of parameters or values changes.
124      */
125     private Map<String, String> getParametersFromRequest(HttpServletRequest request) {
126         Map<String, String> map = new TreeMap<String, String>();
127         for (Object obj : request.getParameterMap().entrySet()) {
128             Map.Entry<String, String[]> entry = (Map.Entry) obj;
129             String[] values = entry.getValue();
130             List<String> newValues = new ArrayList<String>();
131             for (String val : values) {
132                 String str = val.replace(";", ";;");
133                 newValues.add(str);
134             }
135             String value = StringUtils.join(newValues, ";");
136             map.put(entry.getKey(), value);
137         }
138         return map;
139     }
140 
141     public boolean isUseLocale() {
142         return useLocale;
143     }
144 
145     public void setUseLocale(boolean useLocale) {
146         this.useLocale = useLocale;
147     }
148 
149     public boolean isUseRequestMethod() {
150         return useRequestMethod;
151     }
152 
153     public void setUseRequestMethod(boolean useRequestMethod) {
154         this.useRequestMethod = useRequestMethod;
155     }
156 
157     public boolean isUseRequestParameters() {
158         return useRequestParameters;
159     }
160 
161     public void setUseRequestParameters(boolean useRequestParameters) {
162         this.useRequestParameters = useRequestParameters;
163     }
164 
165     public boolean isUseRequestUri() {
166         return useRequestUri;
167     }
168 
169     public void setUseRequestUri(boolean useRequestUri) {
170         this.useRequestUri = useRequestUri;
171     }
172 
173     public boolean isUseRequestGetSecure() {
174         return useRequestGetSecure;
175     }
176 
177     public void setUseRequestGetSecure(boolean useRequestGetSecure) {
178         this.useRequestGetSecure = useRequestGetSecure;
179     }
180 
181     public boolean isUseRequestServerName() {
182         return useRequestServerName;
183     }
184 
185     public void setUseRequestServerName(boolean useRequestServerName) {
186         this.useRequestServerName = useRequestServerName;
187     }
188 
189     public boolean isUseUserName() {
190         return useUserName;
191     }
192 
193     public void setUseUserName(boolean useUserName) {
194         this.useUserName = useUserName;
195     }
196 }