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.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025 026import org.apache.avalon.framework.activity.Disposable; 027import org.apache.avalon.framework.activity.Initializable; 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.configuration.Configuration; 030import org.apache.avalon.framework.configuration.ConfigurationException; 031import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 032import org.apache.avalon.framework.context.Context; 033import org.apache.avalon.framework.context.ContextException; 034import org.apache.avalon.framework.context.Contextualizable; 035import org.apache.avalon.framework.service.ServiceException; 036import org.apache.avalon.framework.service.ServiceManager; 037import org.apache.avalon.framework.service.Serviceable; 038import org.apache.cocoon.components.ContextHelper; 039import org.apache.cocoon.environment.Request; 040import org.apache.excalibur.source.Source; 041import org.apache.excalibur.source.SourceNotFoundException; 042 043import org.ametys.cms.data.holder.group.DataHolderRepeaterDefinitionParser; 044import org.ametys.core.util.filereloader.FileReloader; 045import org.ametys.core.util.filereloader.FileReloaderUtils; 046import org.ametys.runtime.model.Enumerator; 047import org.ametys.runtime.model.View; 048import org.ametys.runtime.model.disableconditions.DisableConditions; 049import org.ametys.runtime.parameter.Validator; 050import org.ametys.runtime.plugin.component.AbstractLogEnabled; 051import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 052import org.ametys.web.WebConstants; 053import org.ametys.web.data.type.ModelItemTypeExtensionPoint; 054import org.ametys.web.parameters.ViewAndParametersParser; 055import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 056import org.ametys.web.parameters.view.GlobalViewParametersManager.ViewParametersType; 057import org.ametys.web.service.Service; 058import org.ametys.web.service.ServiceExtensionPoint; 059import org.ametys.web.source.ServiceSourceFactory; 060 061/** 062 * Manager for service view parameters 063 */ 064public class ServiceViewParametersManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable, Initializable, Disposable 065{ 066 /** Avalon Role */ 067 public static final String ROLE = ServiceViewParametersManager.class.getName(); 068 069 /** The file reloader utils */ 070 protected FileReloaderUtils _fileReloaderUtils; 071 072 /** The view parameter type extension point */ 073 protected ModelItemTypeExtensionPoint _viewParametersEP; 074 075 /** The view and parameters parser */ 076 protected ViewAndParametersParser _viewAndParametersParser; 077 078 /** The service extension point */ 079 protected ServiceExtensionPoint _serviceExtensionPoint; 080 081 /** The avalon context */ 082 protected Context _context; 083 084 /** The service manager */ 085 protected ServiceManager _manager; 086 087 /** The object representing the view parameters of the different service by skin */ 088 protected SkinsServiceViewParameters _skinsServiceViewParameters; 089 090 /** The view parameters manager */ 091 protected ViewParametersManager _viewParametersManager; 092 093 private List<ThreadSafeComponentManager> _components; 094 095 public void service(ServiceManager manager) throws ServiceException 096 { 097 _manager = manager; 098 _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE); 099 _viewParametersEP = (ModelItemTypeExtensionPoint) manager.lookup(ModelItemTypeExtensionPoint.ROLE_VIEW_PARAM); 100 _viewAndParametersParser = (ViewAndParametersParser) manager.lookup(ViewAndParametersParser.ROLE); 101 _serviceExtensionPoint = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 102 _viewParametersManager = (ViewParametersManager) manager.lookup(ViewParametersManager.ROLE); 103 } 104 105 public void contextualize(Context context) throws ContextException 106 { 107 _context = context; 108 } 109 110 public void initialize() throws Exception 111 { 112 _skinsServiceViewParameters = new SkinsServiceViewParameters(); 113 _components = new ArrayList<>(); 114 } 115 116 /** 117 * Get the view parameters of a service 118 * @param skinId the skin id 119 * @param serviceId the service id 120 * @param viewName the view name 121 * @return the view parameters 122 */ 123 public Optional<ViewParametersModel> getViewParameters(String skinId, String serviceId, String viewName) 124 { 125 Service service = _serviceExtensionPoint.getExtension(serviceId); 126 127 String valueWithNoExtension = viewName.substring(0, viewName.length() - 4); 128 String sourceUrl = "service:" + service.getPluginName() + "://" + valueWithNoExtension + ".xml"; 129 130 Request request = ContextHelper.getRequest(_context); 131 Object originalSkinId = request.getAttribute(WebConstants.REQUEST_ATTR_SKIN_ID); 132 try 133 { 134 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, skinId); 135 _fileReloaderUtils.updateFile(sourceUrl, new ServiceViewParametersReloader(skinId, service, viewName, this)); 136 } 137 catch (SourceNotFoundException e) 138 { 139 // Do nothing, the parameter xml file doesn't exist 140 } 141 catch (Exception e) 142 { 143 getLogger().error("Failed to read the service parameters file '{}'. It will be ignored", sourceUrl, e); 144 } 145 finally 146 { 147 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, originalSkinId); 148 } 149 150 Optional<ViewParametersModel> viewParameters = _skinsServiceViewParameters.getServiceViewParameters(skinId, serviceId, viewName); 151 return _viewParametersManager.addGlobalViewParameters(skinId, ViewParametersType.SERVICES, viewParameters); 152 } 153 154 /** 155 * Class representing a service view parameters reloader 156 */ 157 public static class ServiceViewParametersReloader implements FileReloader 158 { 159 private String _skinId; 160 private Service _service; 161 private String _viewName; 162 private ServiceViewParametersManager _serviceViewParameterManager; 163 164 /** 165 * Constructor for the reloader 166 * @param skinId the skin id 167 * @param service the service 168 * @param viewName the service view name 169 * @param manager the service view parameters manager 170 */ 171 public ServiceViewParametersReloader (String skinId, Service service, String viewName, ServiceViewParametersManager manager) 172 { 173 _skinId = skinId; 174 _service = service; 175 _viewName = viewName; 176 _serviceViewParameterManager = manager; 177 } 178 179 /** 180 * Get the skin id 181 * @return the skin id 182 */ 183 public String getSkinId() 184 { 185 return _skinId; 186 } 187 188 /** 189 * Get the service 190 * @return the service 191 */ 192 public Service getService() 193 { 194 return _service; 195 } 196 197 /** 198 * Get the service view name 199 * @return the service view name 200 */ 201 public String getViewName() 202 { 203 return _viewName; 204 } 205 206 /** 207 * Get the service view parameters manager 208 * @return the service view parameters manager 209 */ 210 public ServiceViewParametersManager getServiceViewParameterManager() 211 { 212 return _serviceViewParameterManager; 213 } 214 215 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 216 { 217 if (is != null && source != null) 218 { 219 ServiceViewParametersManager manager = getServiceViewParameterManager(); 220 221 manager._disposeComponents(); 222 223 String plugin = getService().getPluginName(); 224 String catalog = "plugin." + plugin; 225 226 if (source instanceof ServiceSourceFactory.ServiceSource) 227 { 228 if (((ServiceSourceFactory.ServiceSource) source).getSourceType() != ServiceSourceFactory.SourceType.PLUGIN) 229 { 230 catalog = "skin." + getSkinId(); 231 } 232 } 233 234 Configuration conf = new DefaultConfigurationBuilder().build(is); 235 236 String viewParametersId = getSkinId() + "_" + getService().getId() + "_" + getViewName() + "_service_view_parameters"; 237 Configuration paramConfiguration = conf.getChild(ViewParametersManager.VIEW_PARAMETERS_SERVICE_CONF_NAME); 238 239 Optional<ViewParametersModel> viewParameters = manager._configureViewParameters(paramConfiguration, viewParametersId, plugin, catalog); 240 manager._skinsServiceViewParameters.addServiceViewParameters( 241 getSkinId(), 242 getService().getId(), 243 getViewName(), 244 viewParameters 245 ); 246 } 247 } 248 249 public String getId(String sourceUrl) 250 { 251 return ServiceViewParametersManager.class.getName() + "#" + getSkinId() + "_" + getService().getId() + "_" + getViewName(); 252 } 253 } 254 255 /** 256 * Parse service view parameters from the configuration 257 * @param paramConfiguration the configuration 258 * @param viewParametersId the view parameters id 259 * @param plugin the plugin 260 * @param catalog the catalog 261 * @return the view parameters 262 * @throws ConfigurationException if a configuration error occurred 263 */ 264 protected Optional<ViewParametersModel> _configureViewParameters(Configuration paramConfiguration, String viewParametersId, String plugin, String catalog) throws ConfigurationException 265 { 266 ThreadSafeComponentManager<DisableConditions> parametersDisableConditionsManager = new ThreadSafeComponentManager<>(); 267 parametersDisableConditionsManager.setLogger(getLogger()); 268 parametersDisableConditionsManager.contextualize(_context); 269 parametersDisableConditionsManager.service(_manager); 270 _components.add(parametersDisableConditionsManager); 271 272 ThreadSafeComponentManager<DisableConditions> repeaterDisableConditionsManager = new ThreadSafeComponentManager<>(); 273 repeaterDisableConditionsManager.setLogger(getLogger()); 274 repeaterDisableConditionsManager.contextualize(_context); 275 repeaterDisableConditionsManager.service(_manager); 276 _components.add(repeaterDisableConditionsManager); 277 278 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 279 validatorManager.setLogger(getLogger()); 280 validatorManager.contextualize(_context); 281 validatorManager.service(_manager); 282 _components.add(validatorManager); 283 284 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 285 enumeratorManager.setLogger(getLogger()); 286 enumeratorManager.contextualize(_context); 287 enumeratorManager.service(_manager); 288 _components.add(enumeratorManager); 289 290 ViewParametersModel viewParameters = new ViewParametersModel(viewParametersId, new View(), new LinkedHashMap<>()); 291 292 ViewParameterDefinitionParser elementDefinitionParser = new ViewParameterDefinitionParser(_viewParametersEP, parametersDisableConditionsManager, enumeratorManager, validatorManager); 293 DataHolderRepeaterDefinitionParser repeaterDefinitionParser = new DataHolderRepeaterDefinitionParser(_viewParametersEP, repeaterDisableConditionsManager); 294 295 ViewAndParameters viewAndParameters = _viewAndParametersParser.parseParameters(paramConfiguration, plugin, catalog, viewParameters, elementDefinitionParser, repeaterDefinitionParser); 296 viewParameters.setView(viewAndParameters.getView()); 297 viewParameters.setModelItems(viewAndParameters.getParameters()); 298 299 try 300 { 301 elementDefinitionParser.lookupComponents(); 302 repeaterDefinitionParser.lookupComponents(); 303 } 304 catch (Exception e) 305 { 306 throw new ConfigurationException("Unable to lookup parameter local components", paramConfiguration, e); 307 } 308 309 return Optional.ofNullable(viewParameters); 310 } 311 312 public void dispose() 313 { 314 _disposeComponents(); 315 } 316 317 private void _disposeComponents() 318 { 319 for (ThreadSafeComponentManager component : _components) 320 { 321 component.dispose(); 322 } 323 } 324 325 static class SkinsServiceViewParameters 326 { 327 Map<String, SkinWrapper> _servicesBySkin; 328 329 SkinsServiceViewParameters() 330 { 331 this._servicesBySkin = new HashMap<>(); 332 } 333 334 Optional<ViewParametersModel> getServiceViewParameters(String skinId, String serviceId, String viewName) 335 { 336 SkinWrapper skin = _servicesBySkin.getOrDefault(skinId, new SkinWrapper()); 337 return skin.getServiceViewParameters(serviceId, viewName); 338 } 339 340 void addServiceViewParameters(String skinId, String serviceId, String viewName, Optional<ViewParametersModel> viewParameters) 341 { 342 if (!_servicesBySkin.containsKey(skinId)) 343 { 344 _servicesBySkin.put(skinId, new SkinWrapper()); 345 } 346 347 SkinWrapper skin = _servicesBySkin.get(skinId); 348 skin.addServiceViewParameters(serviceId, viewName, viewParameters); 349 } 350 } 351 352 static class SkinWrapper 353 { 354 Map<String, ServiceWrapper> _viewsByService; 355 356 SkinWrapper() 357 { 358 this._viewsByService = new HashMap<>(); 359 } 360 361 Optional<ViewParametersModel> getServiceViewParameters(String serviceId, String viewName) 362 { 363 ServiceWrapper service = _viewsByService.getOrDefault(serviceId, new ServiceWrapper()); 364 return service.getServiceViewParameters(viewName); 365 } 366 367 void addServiceViewParameters(String serviceId, String viewName, Optional<ViewParametersModel> viewParameters) 368 { 369 if (!_viewsByService.containsKey(serviceId)) 370 { 371 _viewsByService.put(serviceId, new ServiceWrapper()); 372 } 373 374 ServiceWrapper service = _viewsByService.get(serviceId); 375 service.addServiceViewParameters(viewName, viewParameters); 376 } 377 } 378 379 static class ServiceWrapper 380 { 381 Map<String, Optional<ViewParametersModel>> _parametersByServiceView; 382 383 ServiceWrapper() 384 { 385 this._parametersByServiceView = new HashMap<>(); 386 } 387 388 Optional<ViewParametersModel> getServiceViewParameters(String viewName) 389 { 390 return _parametersByServiceView.getOrDefault(viewName, Optional.empty()); 391 } 392 393 void addServiceViewParameters(String viewName, Optional<ViewParametersModel> viewParameters) 394 { 395 _parametersByServiceView.put(viewName, viewParameters); 396 } 397 } 398}