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