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}