001/*
002 *  Copyright 2019 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.site;
017
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.Iterator;
021import java.util.List;
022import java.util.Map;
023import java.util.stream.Collectors;
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.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031
032import org.ametys.plugins.repository.UnknownAmetysObjectException;
033import org.ametys.runtime.i18n.I18nizableText;
034import org.ametys.runtime.model.DefinitionAndValue;
035import org.ametys.runtime.model.ElementDefinition;
036import org.ametys.runtime.model.ModelHelper;
037import org.ametys.runtime.model.ModelItem;
038import org.ametys.runtime.model.View;
039import org.ametys.runtime.model.exception.UndefinedItemPathException;
040import org.ametys.runtime.plugin.component.AbstractLogEnabled;
041import org.ametys.web.repository.site.Site;
042import org.ametys.web.repository.site.SiteManager;
043import org.ametys.web.repository.site.SiteType;
044import org.ametys.web.repository.site.SiteTypesExtensionPoint;
045
046/**
047 * Helper component for managing sites configuration.
048 */
049public class SiteConfigurationManager extends AbstractLogEnabled implements Component, Serviceable, Initializable, Disposable
050{
051    /** Avalon Role */
052    public static final String ROLE = SiteConfigurationManager.class.getName();
053    
054    /** The site manager. */
055    protected SiteManager _siteManager;
056    /** The site type extension point. */
057    protected SiteTypesExtensionPoint _siteTypesExtensionPoint;
058    
059    /** Determines if all parameters are valued, by site. */
060    protected Map<String, Boolean> _areSiteComplete;
061    
062    public void service(ServiceManager manager) throws ServiceException
063    {
064        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
065        _siteTypesExtensionPoint = (SiteTypesExtensionPoint) manager.lookup(SiteTypesExtensionPoint.ROLE);
066    }
067
068    public void initialize() throws Exception
069    {
070        _areSiteComplete = new HashMap<>();
071    }
072    
073    public void dispose()
074    {
075        _areSiteComplete = new HashMap<>();
076    }
077    
078    /**
079     * Reload a site's configuration.
080     * @param siteName the site name.
081     * @throws UnknownAmetysObjectException if the site doesn't exist.
082     */
083    public void reloadSiteConfiguration(String siteName) throws UnknownAmetysObjectException
084    {
085        // Check if the site exists.
086        Site site = _siteManager.getSite(siteName);
087        
088        // Reload the site configuration.
089        reloadSiteConfiguration(site);
090    }
091    
092    /**
093     * Reload a site's configuration.
094     * @param site the site.
095     * @throws UnknownAmetysObjectException if the site doesn't exist.
096     */
097    public void reloadSiteConfiguration(Site site) throws UnknownAmetysObjectException
098    {
099        // Reload the site configuration.
100        _areSiteComplete.put(site.getName(), _isSiteConfigurationValid(site));
101    }
102
103    /**
104     * Remove a site's configuration.
105     * @param site the site.
106     */
107    public void removeSiteConfiguration(Site site)
108    {
109        removeSiteConfiguration(site.getName());
110    }
111
112    /**
113     * Remove a site's configuration.
114     * @param siteName the site name.
115     */
116    public void removeSiteConfiguration(String siteName)
117    {
118        if (_areSiteComplete.containsKey(siteName))
119        {
120            _areSiteComplete.remove(siteName);
121        }
122    }
123    
124    /**
125     * Validate the configuration of the given site.
126     * @param siteName the name of the site to check.
127     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
128     * @throws UnknownAmetysObjectException if the site doesn't exist.
129     */
130    public boolean isSiteConfigurationValid(String siteName) throws UnknownAmetysObjectException
131    {
132        if (siteName == null)
133        {
134            throw new IllegalArgumentException("Cannot determine if a null siteName is valid or not");
135        }
136
137        // Check if the site exists.
138        if (!_siteManager.hasSite(siteName))
139        {
140            throw new UnknownAmetysObjectException ("Unknown site '" + siteName + "'. Can not check configuration.");
141        }
142
143        // Validate the site configuration now if it's not already done.
144        Site site = _siteManager.getSite(siteName);
145        return isSiteConfigurationValid(site);
146    }
147    
148    /**
149     * Validate the configuration of the given site.
150     * @param site the site to check.
151     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
152     * @throws UnknownAmetysObjectException if the site doesn't exist.
153     */
154    public boolean isSiteConfigurationValid(Site site) throws UnknownAmetysObjectException
155    {
156        if (site == null)
157        {
158            throw new IllegalArgumentException("Cannot determine if a null site is valid or not");
159        }
160
161        // Validate the site configuration now if it's not already done.
162        if (!_areSiteComplete.containsKey(site.getName()))
163        {
164            _areSiteComplete.put(site.getName(), _isSiteConfigurationValid(site));
165        }
166
167        return _areSiteComplete.get(site.getName());
168    }
169
170    /**
171     * Validate the configuration of the given site.
172     * @param site the site to check.
173     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
174     */
175    protected boolean _isSiteConfigurationValid(Site site)
176    {
177        if (getLogger().isDebugEnabled())
178        {
179            getLogger().debug("Validating the configuration of site '" + site + "'");
180        }
181
182        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
183        if (siteType == null)
184        {
185            getLogger().error("Site " + site.getName() + " has the unknown type '" + site.getType() + "'");
186            return false;
187        }
188        
189        boolean areParametersValid = true;
190
191        Map<String, DefinitionAndValue> definitionAndValues = siteType.getModelItems()
192                .stream()
193                .collect(Collectors.toMap(
194                        m -> m.getName(), 
195                        m -> _modelItemToDefinitionAndValue(site, m))
196                    );
197        Iterator<ElementDefinition> definitionsIterator = siteType.getModelItems().iterator();
198        while (definitionsIterator.hasNext() && areParametersValid)
199        {
200            ElementDefinition item = definitionsIterator.next();
201            boolean isDisabled = ModelHelper.evaluateDisableConditions(item.getDisableConditions(), definitionAndValues, getLogger());
202            if (!isDisabled)
203            {
204                areParametersValid = _isValidSiteParameter(item, site);
205            }
206        }
207        
208        return areParametersValid;
209    }
210    
211    private DefinitionAndValue _modelItemToDefinitionAndValue(Site site, ModelItem modelItem)
212    {
213        Object value = site.getValue(modelItem.getName(), true, null);
214        return new DefinitionAndValue<>(null, modelItem, value);
215    }
216    
217    /**
218     * Validate the the given site parameter
219     * @param parameter the site parameter to validate.
220     * @param site the site
221     * @return true if the parameter's value is valid, false otherwise.
222     */
223    protected boolean _isValidSiteParameter(ElementDefinition parameter, Site site)
224    {
225        // TODO RUNTIME-2897: call the validateValue without boolean when multiple values are managed in enumerators
226        Object value = site.getValue(parameter.getName(), true, null);
227        List<I18nizableText> errors = ModelHelper.validateValue(parameter, value, false);
228        if (!errors.isEmpty())
229        {
230            if (getLogger().isWarnEnabled())
231            {
232                StringBuffer sb = new StringBuffer();
233
234                sb.append("The parameter '")
235                    .append(parameter.getPath())
236                    .append("' of site '")
237                    .append(site.getName())
238                    .append("' is not valid");
239                if (site.hasValue(parameter.getName()))
240                {
241                    sb.append(" with value '");
242                    try
243                    {
244                        sb.append(parameter.getType().toString(value));
245                    }
246                    catch (Exception e)
247                    {
248                        sb.append(value.toString());
249                    }
250                    sb.append("'");
251                }
252                else
253                {
254                    sb.append(" with empty value");
255                }
256                sb.append(":");
257                
258                for (I18nizableText error : errors)
259                {
260                    sb.append("\n* " + error.toString());
261                }
262                
263                sb.append("\nConfiguration is not initialized");
264
265                getLogger().warn(sb.toString());
266            }
267
268            return false;
269        }
270
271        return true;
272    }
273    
274    /**
275     * Retrieves the parameters of the given site
276     * @param siteName the name of the site
277     * @return the site's parameters
278     */
279    public Collection<ElementDefinition> getSiteParameters(String siteName)
280    {
281        Site site = _siteManager.getSite(siteName);
282        return getSiteParameters(site);
283    }
284    
285    /**
286     * Retrieves the parameters of the given site
287     * @param site the site
288     * @return the site's parameters
289     */
290    public Collection<ElementDefinition> getSiteParameters(Site site)
291    {
292        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
293        return siteType.getModelItems();
294    }
295    
296    /**
297     * Retrieves the parameter of the given site with the given name
298     * @param siteName the name of the site
299     * @param parameterName the name of the parameter to retrieve
300     * @return the site's parameter
301     * @throws UndefinedItemPathException if there is no site parameter defined with the given name
302     */
303    public ElementDefinition getSiteParameter(String siteName, String parameterName) throws UndefinedItemPathException
304    {
305        Site site = _siteManager.getSite(siteName);
306        return getSiteParameter(site, parameterName);
307    }
308    
309    /**
310     * Retrieves the parameter of the given site with the given name
311     * @param site the site
312     * @param parameterName the name of the parameter to retrieve
313     * @return the site's parameter
314     * @throws UndefinedItemPathException if there is no site parameter defined with the given name
315     */
316    public ElementDefinition getSiteParameter(Site site, String parameterName) throws UndefinedItemPathException
317    {
318        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
319        return siteType.getModelItem(parameterName);
320    }
321
322    /**
323     * Retrieves the view of the site with the given name
324     * @param siteName the name of the site
325     * @return the site's view
326     */
327    public View getSiteView(String siteName)
328    {
329        Site site = _siteManager.getSite(siteName);
330        return getSiteView(site);
331    }
332
333    /**
334     * Retrieves the view of the site with the given name
335     * @param site the site
336     * @return the site's view
337     */
338    public View getSiteView(Site site)
339    {
340        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
341        return siteType.getView();
342    }
343}