001/* 002 * Copyright 2017 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.core.ui.minimize; 017 018import java.io.UnsupportedEncodingException; 019import java.util.ArrayList; 020import java.util.Base64; 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.activity.Initializable; 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.core.cache.AbstractCacheManager; 034import org.ametys.core.cache.Cache; 035import org.ametys.plugins.core.ui.resources.ResourceDependenciesListExtensionPoint; 036import org.ametys.plugins.core.ui.util.RequestAttributesHelper; 037import org.ametys.runtime.i18n.I18nizableText; 038import org.ametys.runtime.plugin.component.AbstractLogEnabled; 039 040/** 041 * The cache for hashed list of files to minimize 042 */ 043public class HashCache extends AbstractLogEnabled implements Component, Serviceable, Initializable 044{ 045 /** The avalon ROLE */ 046 public static final String ROLE = HashCache.class.getName(); 047 048 /** RequestAttributesHelper */ 049 protected RequestAttributesHelper _requestAttributesHelper; 050 051 /** ResourceDependenciesListExtensionPoint */ 052 protected ResourceDependenciesListExtensionPoint _resourceDependenciesListEP; 053 054 /** CacheManager used to create and get cache */ 055 protected AbstractCacheManager _cacheManager; 056 057 public void service(ServiceManager manager) throws ServiceException 058 { 059 _requestAttributesHelper = (RequestAttributesHelper) manager.lookup(RequestAttributesHelper.ROLE); 060 061 _resourceDependenciesListEP = (ResourceDependenciesListExtensionPoint) manager.lookup(ResourceDependenciesListExtensionPoint.ROLE); 062 063 _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE); 064 } 065 066 public void initialize() throws Exception 067 { 068 _cacheManager.createMemoryCache(ROLE, 069 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_HASH_LABEL"), 070 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_HASH_DESCRIPTION"), 071 true, 072 null); 073 } 074 075 /** 076 * Get the file list corresponding to the given hashcode 077 * @param hash The hashcode created by createHash in this session 078 * @param firstLevel File imported by other files will not be included 079 * @return The list of files or null if the hashcode does not exists 080 */ 081 public List<UriData> getFilesForHash(String hash, boolean firstLevel) 082 { 083 List<UriData> uriDataList = _getCache().get(hash); 084 if (firstLevel) 085 { 086 return uriDataList.stream() 087 .filter(UriData::isFirstLevel) 088 .collect(Collectors.toList()); 089 } 090 return uriDataList; 091 } 092 093 /** 094 * Creates and memorize a hash code corresponding to a list of files 095 * @param files The files. Key is the file uri and the value is a map with two keys 'media' and 'tag'. 096 * @param salt Additional parameter used as salt for the hash key. 097 * @return The hash code 098 * @throws IllegalArgumentException If an error occurred 099 */ 100 public String createHash(Map<String, Map<String, String>> files, String salt) throws IllegalArgumentException 101 { 102 List<UriData> hashCache = new ArrayList<>(); 103 104 Map<String, Object> attributes = _requestAttributesHelper.saveRequestAttributes(); 105 106 try 107 { 108 files.entrySet().stream() 109 .map(file -> _resourceDependenciesListEP.getDependencies(file.getKey(), file.getValue())) 110 .filter(Objects::nonNull) 111 .forEach(fileDependencies -> hashCache.addAll(fileDependencies)); 112 113 String hash = Base64.getEncoder().withoutPadding().encodeToString(String.valueOf(31 * hashCache.hashCode() + salt.hashCode()).getBytes("UTF-8")); 114 _getCache().put(hash, hashCache); 115 return hash; 116 } 117 catch (UnsupportedEncodingException e) 118 { 119 throw new IllegalArgumentException(e); 120 } 121 finally 122 { 123 _requestAttributesHelper.restoreRequestAttributes(attributes); 124 } 125 } 126 127 /** 128 * The description of an URI 129 */ 130 public static class UriData 131 { 132 private String _uri; 133 private Long _lastModified; 134 private String _media; 135 private boolean _firstLevel; 136 137 /** 138 * Default constructor for a file data 139 * @param uri The uri locating the file 140 * @param firstLevel False if the file data is an import from another file data 141 */ 142 public UriData(String uri, boolean firstLevel) 143 { 144 _uri = uri; 145 _firstLevel = firstLevel; 146 } 147 148 /** 149 * Set the last modified value 150 * @param lastModified the lastModified to set 151 */ 152 public void setLastModified(Long lastModified) 153 { 154 this._lastModified = lastModified; 155 } 156 157 /** 158 * set the medias value 159 * @param media the medias to set 160 */ 161 public void setMedia(String media) 162 { 163 this._media = media; 164 } 165 166 /** 167 * Get the file uri 168 * @return the uri 169 */ 170 public String getUri() 171 { 172 return _uri; 173 } 174 175 /** 176 * Get the file last modified date 177 * @return the lastModified 178 */ 179 public Long getLastModified() 180 { 181 return _lastModified; 182 } 183 184 /** 185 * Get the file medias 186 * @return the medias 187 */ 188 public String getMedia() 189 { 190 return _media; 191 } 192 193 /** 194 * Check if the file is a first level file 195 * @return True if the file is first level 196 */ 197 public boolean isFirstLevel() 198 { 199 return _firstLevel; 200 } 201 202 @Override 203 public boolean equals(Object obj) 204 { 205 if (obj instanceof UriData) 206 { 207 UriData fObj = (UriData) obj; 208 return StringUtils.equals(_uri, fObj._uri) 209 && (_lastModified == null ? fObj._lastModified == null : _lastModified.equals(fObj._lastModified)) 210 && StringUtils.equals(_media, fObj._media); 211 } 212 return false; 213 } 214 215 @Override 216 public int hashCode() 217 { 218 return Objects.hash(_uri, _lastModified, _media); 219 } 220 221 @Override 222 public String toString() 223 { 224 if (_media != null) 225 { 226 return _uri + "#" + _media + " (" + _lastModified + ")"; 227 } 228 else 229 { 230 return _uri + " (" + _lastModified + ")"; 231 } 232 } 233 } 234 235 private Cache<String, List<UriData>> _getCache() 236 { 237 return this._cacheManager.get(ROLE); 238 } 239}