001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.staticexport; 029 030import org.opencms.configuration.CmsConfigurationException; 031 032import java.util.ArrayList; 033import java.util.Collections; 034import java.util.HashSet; 035import java.util.List; 036import java.util.Locale; 037import java.util.Set; 038import java.util.regex.Pattern; 039 040/** 041 * Configuration for the shared cache delivery mode.<p> 042 */ 043public class CmsSharedCacheConfiguration { 044 045 /** Pattern for an exact MIME type or a top-level wildcard. */ 046 private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile( 047 "[a-z0-9!#$&^_.+-]+/(?:\\*|[a-z0-9!#$&^_.+-]+)"); 048 049 /** If this configuration was explicitly configured. */ 050 private boolean m_configured; 051 052 /** If shared cache delivery is enabled. */ 053 private boolean m_enabled; 054 055 /** The configured cache policies. */ 056 private List<CmsSharedCachePolicy> m_policies = new ArrayList<CmsSharedCachePolicy>(); 057 058 /** 059 * Adds a cache policy.<p> 060 * 061 * @param policy the cache policy 062 */ 063 public void addCachePolicy(CmsSharedCachePolicy policy) { 064 065 if (policy != null) { 066 m_policies.add(policy); 067 m_configured = true; 068 } 069 } 070 071 /** 072 * Returns the configured cache policies in configuration order.<p> 073 * 074 * @return the configured cache policies 075 */ 076 public List<CmsSharedCachePolicy> getCachePolicies() { 077 078 return Collections.unmodifiableList(m_policies); 079 } 080 081 /** 082 * Returns the cache policy for a content type.<p> 083 * 084 * Exact content type policies take precedence over top-level wildcard policies. The default policy is returned 085 * if no content type policy matches.<p> 086 * 087 * @param contentType the content type 088 * @return the matching cache policy, or the default policy 089 */ 090 public CmsSharedCachePolicy getCachePolicy(String contentType) { 091 092 String normalizedContentType = normalizeContentType(contentType); 093 CmsSharedCachePolicy wildcardPolicy = null; 094 if (normalizedContentType != null) { 095 int separator = normalizedContentType.indexOf('/'); 096 String wildcard = separator < 0 ? null : normalizedContentType.substring(0, separator) + "/*"; 097 for (CmsSharedCachePolicy policy : m_policies) { 098 if (normalizedContentType.equals(policy.getContentType())) { 099 return policy; 100 } 101 if ((wildcard != null) && wildcard.equals(policy.getContentType())) { 102 wildcardPolicy = policy; 103 } 104 } 105 } 106 return wildcardPolicy != null ? wildcardPolicy : getDefaultCachePolicy(); 107 } 108 109 /** 110 * Returns the default cache policy.<p> 111 * 112 * @return the default cache policy, or {@code null} if none is configured 113 */ 114 public CmsSharedCachePolicy getDefaultCachePolicy() { 115 116 for (CmsSharedCachePolicy policy : m_policies) { 117 if (policy.isDefault()) { 118 return policy; 119 } 120 } 121 return null; 122 } 123 124 /** 125 * Returns whether shared cache settings have been configured explicitly.<p> 126 * 127 * @return {@code true} if shared cache settings have been configured 128 */ 129 public boolean isConfigured() { 130 131 return m_configured; 132 } 133 134 /** 135 * Returns whether shared cache delivery is enabled.<p> 136 * 137 * @return {@code true} if shared cache delivery is enabled 138 */ 139 public boolean isEnabled() { 140 141 return m_enabled; 142 } 143 144 /** 145 * Sets whether shared cache delivery is enabled.<p> 146 * 147 * @param enabled if shared cache delivery is enabled 148 */ 149 public void setEnabled(String enabled) { 150 151 m_enabled = Boolean.valueOf(enabled).booleanValue(); 152 m_configured = true; 153 } 154 155 /** 156 * Validates the shared cache configuration.<p> 157 * 158 * @throws CmsConfigurationException if the configuration is invalid 159 */ 160 public void validate() throws CmsConfigurationException { 161 162 if (!m_configured) { 163 return; 164 } 165 int defaultPolicyCount = 0; 166 Set<String> contentTypes = new HashSet<String>(); 167 for (CmsSharedCachePolicy policy : m_policies) { 168 String policyName = policy.isDefault() ? "default" : policy.getContentType(); 169 if (policy.isDefault()) { 170 defaultPolicyCount++; 171 } else { 172 if (!CONTENT_TYPE_PATTERN.matcher(policy.getContentType()).matches()) { 173 throw new CmsConfigurationException( 174 org.opencms.configuration.Messages.get().container( 175 org.opencms.configuration.Messages.ERR_SHAREDCACHE_POLICY_CONTENTTYPE_1, 176 policy.getContentType())); 177 } 178 if (!contentTypes.add(policy.getContentType())) { 179 throw new CmsConfigurationException( 180 org.opencms.configuration.Messages.get().container( 181 org.opencms.configuration.Messages.ERR_SHAREDCACHE_POLICY_DUPLICATE_1, 182 policy.getContentType())); 183 } 184 } 185 validateDuration("clientmaxage", policyName, policy.getClientMaxAge(), false); 186 validateDuration("sharedmaxage", policyName, policy.getSharedMaxAge(), false); 187 validateDuration("staleiferror", policyName, policy.getStaleIfError(), true); 188 } 189 if (defaultPolicyCount != 1) { 190 throw new CmsConfigurationException( 191 org.opencms.configuration.Messages.get().container( 192 org.opencms.configuration.Messages.ERR_SHAREDCACHE_DEFAULT_POLICY_1, 193 Integer.valueOf(defaultPolicyCount))); 194 } 195 } 196 197 /** 198 * Normalizes a response content type for matching.<p> 199 * 200 * @param contentType the content type 201 * @return the normalized content type 202 */ 203 private String normalizeContentType(String contentType) { 204 205 if (contentType == null) { 206 return null; 207 } 208 int parameterSeparator = contentType.indexOf(';'); 209 String result = parameterSeparator < 0 ? contentType : contentType.substring(0, parameterSeparator); 210 result = result.trim().toLowerCase(Locale.ROOT); 211 return result.isEmpty() ? null : result; 212 } 213 214 /** 215 * Validates a configured duration.<p> 216 * 217 * @param fieldName the XML field name 218 * @param policyName the policy name 219 * @param value the configured value 220 * @param optional whether the duration is optional 221 * 222 * @throws CmsConfigurationException if the duration is invalid 223 */ 224 private void validateDuration(String fieldName, String policyName, long value, boolean optional) 225 throws CmsConfigurationException { 226 227 if ((value < 0) && !(optional && (value == CmsSharedCachePolicy.DURATION_UNSET))) { 228 throw new CmsConfigurationException( 229 org.opencms.configuration.Messages.get().container( 230 org.opencms.configuration.Messages.ERR_SHAREDCACHE_POLICY_DURATION_2, 231 fieldName, 232 policyName)); 233 } 234 } 235}