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