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