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