001/* 002 * Copyright 2024 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.skin; 017 018import java.io.InputStream; 019 020import org.apache.avalon.framework.activity.Initializable; 021import org.apache.avalon.framework.component.Component; 022import org.apache.avalon.framework.configuration.Configuration; 023import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 024import org.apache.avalon.framework.context.Context; 025import org.apache.avalon.framework.context.ContextException; 026import org.apache.avalon.framework.context.Contextualizable; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.cocoon.components.ContextHelper; 031import org.apache.cocoon.environment.Request; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.excalibur.source.Source; 034 035import org.ametys.core.cache.AbstractCacheManager; 036import org.ametys.core.cache.Cache; 037import org.ametys.core.source.OptionalSourceFallback; 038import org.ametys.core.source.StaticOptionalSourceFallback; 039import org.ametys.core.source.StaticOptionalSourceFallback.Fallbacks; 040import org.ametys.core.util.filereloader.FileReloader; 041import org.ametys.core.util.filereloader.FileReloaderUtils; 042import org.ametys.runtime.i18n.I18nizableText; 043import org.ametys.web.WebConstants; 044import org.ametys.web.WebHelper; 045import org.ametys.web.repository.site.Site; 046import org.ametys.web.repository.site.SiteManager; 047 048/** 049 * This implementation will read its configuration from the current skin in conf/optional-source-fallbacks.xml 050 */ 051public class SkinOptionalSourceFallback implements OptionalSourceFallback, Component, Serviceable, Initializable, Contextualizable, FileReloader 052{ 053 private static final String _CACHE_ID = SkinOptionalSourceFallback.class.getName(); 054 055 private FileReloaderUtils _fileReloaderUtils; 056 private AbstractCacheManager _cacheManager; 057 private Context _context; 058 private SiteManager _siteManager; 059 060 public void service(ServiceManager manager) throws ServiceException 061 { 062 _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE); 063 _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE); 064 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 065 } 066 067 public void contextualize(Context context) throws ContextException 068 { 069 _context = context; 070 } 071 072 public void initialize() throws Exception 073 { 074 _cacheManager.createMemoryCache(_CACHE_ID, 075 new I18nizableText("plugin.web", "PLUGINS_WEB_SKIN_OPTIONAL_SOURCE_FALLBACKS_CACHE_LABEL"), 076 new I18nizableText("plugin.web", "PLUGINS_WEB_SKIN_OPTIONAL_SOURCE_FALLBACKS_CACHE_DESCRIPTION"), 077 true, 078 null); 079 } 080 081 public String fallback(String uri) 082 { 083 Request request = ContextHelper.getRequest(_context); 084 String skinId = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SKIN_ID); 085 if (skinId == null) 086 { 087 String currentSiteName = WebHelper.getSiteName(request); 088 Site currentSite = _siteManager.getSite(currentSiteName); 089 if (currentSite != null) 090 { 091 skinId = currentSite.getSkinId(); 092 } 093 } 094 095 if (skinId != null) 096 { 097 String sourceUri = "skin:" + skinId + "://conf/optional-source-fallbacks.xml"; 098 099 boolean forceReload = !_getCache().hasKey(sourceUri); 100 try 101 { 102 _fileReloaderUtils.updateFile(sourceUri, forceReload, this); 103 } 104 catch (Exception e) 105 { 106 throw new RuntimeException("Cannot load file '" + sourceUri + "'", e); 107 } 108 109 StaticOptionalSourceFallback.Fallbacks fallbacks = _getCache().get(sourceUri); 110 if (fallbacks != null) 111 { 112 return StaticOptionalSourceFallback.applyFallbacks(uri, fallbacks); 113 } 114 } 115 116 return null; 117 } 118 119 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 120 { 121 Fallbacks fallbacks = null; 122 123 if (is != null) 124 { 125 Configuration configuration = new DefaultConfigurationBuilder().build(source.getInputStream()); 126 fallbacks = StaticOptionalSourceFallback.configureFallbacks(configuration, StringUtils.substringBefore(sourceUrl, "conf")); 127 } 128 129 _getCache().put(sourceUrl, fallbacks); 130 } 131 132 public String getId(String sourceUrl) 133 { 134 return sourceUrl; 135 } 136 137 private Cache<String, StaticOptionalSourceFallback.Fallbacks> _getCache() 138 { 139 return _cacheManager.get(_CACHE_ID); 140 } 141}