001/* 002 * Copyright 2021 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.web.sitemap; 017 018import java.io.InputStream; 019import java.net.MalformedURLException; 020import java.util.Collections; 021import java.util.LinkedHashSet; 022import java.util.Set; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026import org.apache.avalon.framework.activity.Initializable; 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.excalibur.source.Source; 033import org.apache.excalibur.source.SourceResolver; 034 035import org.ametys.core.cache.AbstractCacheManager; 036import org.ametys.core.cache.Cache; 037import org.ametys.core.util.filereloader.FileReloader; 038import org.ametys.core.util.filereloader.FileReloaderUtils; 039import org.ametys.runtime.i18n.I18nizableText; 040import org.ametys.runtime.plugin.component.AbstractLogEnabled; 041import org.ametys.web.skin.SkinsManager; 042 043/** 044 * Implementation of {@link SitemapIcons} for icons provided by skins 045 */ 046public class SkinSitemapIcons extends AbstractLogEnabled implements SitemapIcons, Initializable, Serviceable, FileReloader 047{ 048 private static final Pattern __SKIN_SOURCE_PATTERN = Pattern.compile("^skin:(.*)://(.*)$"); 049 private static final String __ICON_CACHE = SkinSitemapIcons.class.getName() + "$iconCache"; 050 051 /** The skins manager */ 052 protected SkinsManager _skinsManager; 053 /** The source resolver */ 054 protected SourceResolver _srcResolver; 055 056 private AbstractCacheManager _cacheManager; 057 private FileReloaderUtils _fileReloader; 058 059 @Override 060 public void service(ServiceManager smanager) throws ServiceException 061 { 062 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 063 _srcResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE); 064 _cacheManager = (AbstractCacheManager) smanager.lookup(AbstractCacheManager.ROLE); 065 _fileReloader = (FileReloaderUtils) smanager.lookup(FileReloaderUtils.ROLE); 066 } 067 068 @Override 069 public void initialize() throws Exception 070 { 071 _createCaches(); 072 } 073 074 /** 075 * Creates the caches 076 */ 077 protected void _createCaches() 078 { 079 if (!_cacheManager.hasCache(__ICON_CACHE)) 080 { 081 _cacheManager.createMemoryCache(__ICON_CACHE, 082 new I18nizableText("plugin.web", "PLUGINS_WEB_SITEMAP_ICONS_CACHE_LABEL"), 083 new I18nizableText("plugin.web", "PLUGINS_WEB_SITEMAP_ICONS_CACHE_DESCRIPTION"), 084 true, 085 null); 086 } 087 } 088 089 @Override 090 public Set<SitemapIcon> getIcons(String skinName) 091 { 092 try 093 { 094 boolean force = !_getIconCache().hasKey(skinName); 095 _fileReloader.updateFile("skin:" + skinName + "://conf/sitemap-icons.xml", force, this); 096 } 097 catch (Exception e) 098 { 099 getLogger().error("Unable to read the available sitemap icons configuration file for skin '{}'", e); 100 } 101 102 Set<SitemapIcon> icons = _getIconCache().get(skinName); 103 return icons != null ? icons : Collections.emptySet(); 104 } 105 106 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 107 { 108 Matcher m = __SKIN_SOURCE_PATTERN.matcher(sourceUrl); 109 if (!m.matches()) 110 { 111 throw new MalformedURLException("URI must be like skin:<name>://path/to/resource. Location was '" + sourceUrl + "'"); 112 } 113 114 String skinId = m.group(1); 115 Set<SitemapIcon> icons = new LinkedHashSet<>(); 116 117 if (is != null) 118 { 119 Configuration configuration = new DefaultConfigurationBuilder().build(is); 120 for (Configuration iconConfiguration : configuration.getChildren("icon")) 121 { 122 SitemapIcon icon = new SitemapIcon(); 123 icon.configure(iconConfiguration, "skin/" + skinId + "/resources/"); 124 icons.add(icon); 125 } 126 } 127 128 // Fill cache 129 Cache<String, Set<SitemapIcon>> iconCache = _getIconCache(); 130 iconCache.put(skinId, icons); 131 } 132 133 public String getId(String sourceUrl) 134 { 135 return sourceUrl; 136 } 137 138 private Cache<String, Set<SitemapIcon>> _getIconCache() 139 { 140 return _cacheManager.get(__ICON_CACHE); 141 } 142}