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.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Objects; 025import java.util.stream.Collectors; 026 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.plugins.core.ui.resources.ResourceDependenciesListExtensionPoint; 034import org.ametys.plugins.core.ui.util.RequestAttributesHelper; 035import org.ametys.runtime.plugin.component.AbstractLogEnabled; 036 037/** 038 * The cache for hashed list of files to minimize 039 */ 040public class HashCache extends AbstractLogEnabled implements Component, Serviceable 041{ 042 /** The avalon ROLE */ 043 public static final String ROLE = HashCache.class.getName(); 044 045 /** RequestAttributesHelper */ 046 protected RequestAttributesHelper _requestAttributesHelper; 047 048 /** ResourceDependenciesListExtensionPoint */ 049 protected ResourceDependenciesListExtensionPoint _resourceDependenciesListEP; 050 051 /** 052 * Hash cache 053 * Key if file url 054 */ 055 private Map<String, List<UriData>> _hashCache = new HashMap<>(); 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 064 /** 065 * Get the file list corresponding to the given hashcode 066 * @param hash The hashcode created by createHash in this session 067 * @param firstLevel File imported by other files will not be included 068 * @return The list of files or null if the hashcode does not exists 069 */ 070 public List<UriData> getFilesForHash(String hash, boolean firstLevel) 071 { 072 if (firstLevel) 073 { 074 return _hashCache.get(hash).stream() 075 .filter(UriData::isFirstLevel) 076 .collect(Collectors.toList()); 077 } 078 return _hashCache.get(hash); 079 } 080 081 /** 082 * Creates and memorize a hash code corresponding to a list of files 083 * @param files The files. Key is the file uri and the value is a map with two keys 'media' and 'tag'. 084 * @param salt Additional parameter used as salt for the hash key. 085 * @return The hash code 086 * @throws IllegalArgumentException If an error occurred 087 */ 088 public String createHash(Map<String, Map<String, String>> files, String salt) throws IllegalArgumentException 089 { 090 List<UriData> hashCache = new ArrayList<>(); 091 092 Map<String, Object> attributes = _requestAttributesHelper.saveRequestAttributes(); 093 094 try 095 { 096 files.entrySet().stream() 097 .map(file -> _resourceDependenciesListEP.getDependencies(file.getKey(), file.getValue())) 098 .filter(Objects::nonNull) 099 .forEach(fileDependencies -> hashCache.addAll(fileDependencies)); 100 101 String hash = Base64.getEncoder().withoutPadding().encodeToString(String.valueOf(31 * hashCache.hashCode() + salt.hashCode()).getBytes("UTF-8")); 102 _hashCache.put(hash, hashCache); 103 return hash; 104 } 105 catch (UnsupportedEncodingException e) 106 { 107 throw new IllegalArgumentException(e); 108 } 109 finally 110 { 111 _requestAttributesHelper.restoreRequestAttributes(attributes); 112 } 113 } 114 115 /** 116 * The description of an URI 117 */ 118 public static class UriData 119 { 120 private String _uri; 121 private Long _lastModified; 122 private String _media; 123 private boolean _firstLevel; 124 125 /** 126 * Default constructor for a file data 127 * @param uri The uri locating the file 128 * @param firstLevel False if the file data is an import from another file data 129 */ 130 public UriData(String uri, boolean firstLevel) 131 { 132 _uri = uri; 133 _firstLevel = firstLevel; 134 } 135 136 /** 137 * Set the last modified value 138 * @param lastModified the lastModified to set 139 */ 140 public void setLastModified(Long lastModified) 141 { 142 this._lastModified = lastModified; 143 } 144 145 /** 146 * set the medias value 147 * @param media the medias to set 148 */ 149 public void setMedia(String media) 150 { 151 this._media = media; 152 } 153 154 /** 155 * Get the file uri 156 * @return the uri 157 */ 158 public String getUri() 159 { 160 return _uri; 161 } 162 163 /** 164 * Get the file last modified date 165 * @return the lastModified 166 */ 167 public Long getLastModified() 168 { 169 return _lastModified; 170 } 171 172 /** 173 * Get the file medias 174 * @return the medias 175 */ 176 public String getMedia() 177 { 178 return _media; 179 } 180 181 /** 182 * Check if the file is a first level file 183 * @return True if the file is first level 184 */ 185 public boolean isFirstLevel() 186 { 187 return _firstLevel; 188 } 189 190 @Override 191 public boolean equals(Object obj) 192 { 193 if (obj instanceof UriData) 194 { 195 UriData fObj = (UriData) obj; 196 return StringUtils.equals(_uri, fObj._uri) 197 && (_lastModified == null ? fObj._lastModified == null : _lastModified.equals(fObj._lastModified)) 198 && StringUtils.equals(_media, fObj._media); 199 } 200 return false; 201 } 202 203 @Override 204 public int hashCode() 205 { 206 return Objects.hash(_uri, _lastModified, _media); 207 } 208 209 @Override 210 public String toString() 211 { 212 if (_media != null) 213 { 214 return _uri + "#" + _media + " (" + _lastModified + ")"; 215 } 216 else 217 { 218 return _uri + " (" + _lastModified + ")"; 219 } 220 } 221 } 222}