001/* 002 * Copyright 2020 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.parameters.view; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Optional; 024 025import org.apache.avalon.framework.activity.Disposable; 026import org.apache.avalon.framework.activity.Initializable; 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.avalon.framework.configuration.ConfigurationException; 030import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 031import org.apache.avalon.framework.context.Context; 032import org.apache.avalon.framework.context.ContextException; 033import org.apache.avalon.framework.context.Contextualizable; 034import org.apache.avalon.framework.service.ServiceException; 035import org.apache.avalon.framework.service.ServiceManager; 036import org.apache.avalon.framework.service.Serviceable; 037import org.apache.commons.lang.StringUtils; 038import org.apache.excalibur.source.Source; 039import org.apache.excalibur.source.SourceNotFoundException; 040 041import org.ametys.core.util.filereloader.FileReloader; 042import org.ametys.core.util.filereloader.FileReloaderUtils; 043import org.ametys.plugins.repository.model.parsing.RepeaterDefinitionParser; 044import org.ametys.runtime.model.Enumerator; 045import org.ametys.runtime.model.View; 046import org.ametys.runtime.parameter.Validator; 047import org.ametys.runtime.plugin.component.AbstractLogEnabled; 048import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 049import org.ametys.web.parameters.ViewAndParametersParser; 050import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 051 052/** 053 * Manager for global view parameters 054 */ 055public class GlobalViewParametersManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable, Initializable, Disposable 056{ 057 /** Avalon Role */ 058 public static final String ROLE = GlobalViewParametersManager.class.getName(); 059 060 /** The file reloader utils */ 061 protected FileReloaderUtils _fileReloaderUtils; 062 063 /** The view parameter type extension point */ 064 protected ViewParameterTypeExtensionPoint _viewParametersEP; 065 066 /** The view and parameters parser */ 067 protected ViewAndParametersParser _viewAndParametersParser; 068 069 /** The avalon context */ 070 protected Context _context; 071 072 /** The service manager */ 073 protected ServiceManager _manager; 074 075 /** The object representing the global view parameters by skin */ 076 protected SkinsGlobalViewParameters _skinsGlobalViewParameters; 077 078 private List<ThreadSafeComponentManager> _components; 079 080 public void service(ServiceManager manager) throws ServiceException 081 { 082 _manager = manager; 083 _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE); 084 _viewParametersEP = (ViewParameterTypeExtensionPoint) manager.lookup(ViewParameterTypeExtensionPoint.ROLE); 085 _viewAndParametersParser = (ViewAndParametersParser) manager.lookup(ViewAndParametersParser.ROLE); 086 } 087 088 public void contextualize(Context context) throws ContextException 089 { 090 _context = context; 091 } 092 093 public void initialize() throws Exception 094 { 095 _skinsGlobalViewParameters = new SkinsGlobalViewParameters(); 096 _components = new ArrayList<>(); 097 } 098 099 /** 100 * The view parameters type 101 */ 102 public enum ViewParametersType 103 { 104 /** View parameters for template */ 105 TEMPLATES, 106 /** View parameters for zone */ 107 ZONES, 108 /** View parameters for zone item */ 109 ZONEITEMS, 110 /** View parameters for service */ 111 SERVICES, 112 /** View parameters for content */ 113 CONTENTS 114 } 115 116 /** 117 * Get global view parameters for one type (template, zone, zone item, service or content) 118 * @param skinId the skin id 119 * @param type the type 120 * @return the view parameters 121 */ 122 public Optional<ViewParametersModel> getViewParameters(String skinId, ViewParametersType type) 123 { 124 String sourceUrl = "skin:" + skinId + "://conf/parameters.xml"; 125 try 126 { 127 _fileReloaderUtils.updateFile(sourceUrl, new GlobalViewParametersReloader(skinId, this)); 128 } 129 catch (SourceNotFoundException e) 130 { 131 // Do nothing, the parameter xml file doesn't exist 132 } 133 catch (Exception e) 134 { 135 getLogger().error("Failed to read the global parameters file '{}'. It will be ignored", sourceUrl, e); 136 } 137 138 return _skinsGlobalViewParameters.getGlobalViewParameters(skinId, type); 139 } 140 141 /** 142 * Class representing a global view parameters reloader 143 */ 144 public static class GlobalViewParametersReloader implements FileReloader 145 { 146 private String _skinId; 147 private GlobalViewParametersManager _globalViewParameterManager; 148 149 /** 150 * Constructor for the reloader 151 * @param skinId the skin id 152 * @param manager the global view parameters manager 153 */ 154 public GlobalViewParametersReloader (String skinId, GlobalViewParametersManager manager) 155 { 156 _skinId = skinId; 157 _globalViewParameterManager = manager; 158 } 159 160 /** 161 * Get the skin id 162 * @return the skin id 163 */ 164 public String getSkinId() 165 { 166 return _skinId; 167 } 168 169 /** 170 * Get the global view parameters manager 171 * @return the global view parameters manager 172 */ 173 public GlobalViewParametersManager getGlobalViewParameterManager() 174 { 175 return _globalViewParameterManager; 176 } 177 178 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 179 { 180 if (is != null) 181 { 182 GlobalViewParametersManager manager = getGlobalViewParameterManager(); 183 184 manager._disposeComponents(); 185 186 String skinId = getSkinId(); 187 String plugin = "web"; // default plugin for enumerator and validator component 188 String catalog = "skin." + getSkinId(); 189 190 Configuration conf = new DefaultConfigurationBuilder().build(is); 191 192 manager._addViewParameters(conf, skinId, ViewParametersType.TEMPLATES, plugin, catalog); 193 manager._addViewParameters(conf, skinId, ViewParametersType.ZONES, plugin, catalog); 194 manager._addViewParameters(conf, skinId, ViewParametersType.ZONEITEMS, plugin, catalog); 195 manager._addViewParameters(conf, skinId, ViewParametersType.SERVICES, plugin, catalog); 196 manager._addViewParameters(conf, skinId, ViewParametersType.CONTENTS, plugin, catalog); 197 198 } 199 } 200 201 public String getId(String sourceUrl) 202 { 203 return GlobalViewParametersManager.class.getName() + "#" + getSkinId(); 204 } 205 } 206 207 /** 208 * Add view parameters from the configuration of a given type 209 * @param paramConfiguration the configuration 210 * @param skinId the skin Id 211 * @param type the type 212 * @param plugin the plugin 213 * @param catalog the catalog 214 * @throws ConfigurationException if a configuration error occurred 215 */ 216 protected void _addViewParameters(Configuration paramConfiguration, String skinId, ViewParametersType type, String plugin, String catalog) throws ConfigurationException 217 { 218 String viewParametersId = skinId + "_" + StringUtils.lowerCase(type.name()) + "_global_view_parameters"; 219 220 Configuration typeParamConfiguration = paramConfiguration.getChild(StringUtils.lowerCase(type.name())); 221 Optional<ViewParametersModel> viewParameters = _configureViewParameters(typeParamConfiguration, viewParametersId, plugin, catalog); 222 223 _skinsGlobalViewParameters.addGlobalViewParameters(skinId, type, viewParameters); 224 } 225 226 /** 227 * Parse global view parameters from the configuration 228 * @param paramConfiguration the configuration 229 * @param viewParametersId the view parameters id 230 * @param plugin the plugin 231 * @param catalog the catalog 232 * @return the view parameters 233 * @throws ConfigurationException if a configuration error occurred 234 */ 235 protected Optional<ViewParametersModel> _configureViewParameters(Configuration paramConfiguration, String viewParametersId, String plugin, String catalog) throws ConfigurationException 236 { 237 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 238 validatorManager.setLogger(getLogger()); 239 validatorManager.contextualize(_context); 240 validatorManager.service(_manager); 241 _components.add(validatorManager); 242 243 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 244 enumeratorManager.setLogger(getLogger()); 245 enumeratorManager.contextualize(_context); 246 enumeratorManager.service(_manager); 247 _components.add(enumeratorManager); 248 249 ViewParametersModel viewParameters = new ViewParametersModel(viewParametersId, new View(), new HashMap<>()); 250 251 ViewParameterDefinitionParser elementDefinitionParser = new ViewParameterDefinitionParser(_viewParametersEP, enumeratorManager, validatorManager); 252 RepeaterDefinitionParser repeaterDefinitionParser = new RepeaterDefinitionParser(_viewParametersEP); 253 254 ViewAndParameters viewAndParameters = _viewAndParametersParser.parseParameters(paramConfiguration, plugin, catalog, viewParameters, elementDefinitionParser, repeaterDefinitionParser); 255 viewParameters.setView(viewAndParameters.getView()); 256 viewParameters.setModelItems(viewAndParameters.getParameters()); 257 258 try 259 { 260 elementDefinitionParser.lookupComponents(); 261 } 262 catch (Exception e) 263 { 264 throw new ConfigurationException("Unable to lookup parameter local components", paramConfiguration, e); 265 } 266 267 return Optional.ofNullable(viewParameters); 268 } 269 270 public void dispose() 271 { 272 _disposeComponents(); 273 } 274 275 private void _disposeComponents() 276 { 277 for (ThreadSafeComponentManager component : _components) 278 { 279 component.dispose(); 280 } 281 } 282 283 static class SkinsGlobalViewParameters 284 { 285 Map<String, SkinWrapper> _globalViewParametersBySkin; 286 287 SkinsGlobalViewParameters() 288 { 289 this._globalViewParametersBySkin = new HashMap<>(); 290 } 291 292 Optional<ViewParametersModel> getGlobalViewParameters(String skinId, ViewParametersType type) 293 { 294 SkinWrapper skin = _globalViewParametersBySkin.getOrDefault(skinId, new SkinWrapper()); 295 return skin.getGlobalViewParameters(type); 296 } 297 298 void addGlobalViewParameters(String skinId, ViewParametersType type, Optional<ViewParametersModel> viewParameters) 299 { 300 if (!_globalViewParametersBySkin.containsKey(skinId)) 301 { 302 _globalViewParametersBySkin.put(skinId, new SkinWrapper()); 303 } 304 305 SkinWrapper skin = _globalViewParametersBySkin.get(skinId); 306 skin.addGlobalViewParameters(type, viewParameters); 307 } 308 } 309 310 static class SkinWrapper 311 { 312 Map<ViewParametersType, Optional<ViewParametersModel>> _parametersByType; 313 314 SkinWrapper() 315 { 316 this._parametersByType = new HashMap<>(); 317 } 318 319 Optional<ViewParametersModel> getGlobalViewParameters(ViewParametersType type) 320 { 321 return _parametersByType.getOrDefault(type, Optional.empty()); 322 } 323 324 void addGlobalViewParameters(ViewParametersType type, Optional<ViewParametersModel> viewParameters) 325 { 326 _parametersByType.put(type, viewParameters); 327 } 328 } 329}