001/*
002 *  Copyright 2016 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.plugins.core.impl.schedule;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.configuration.Configurable;
024import org.apache.avalon.framework.configuration.Configuration;
025import org.apache.avalon.framework.configuration.ConfigurationException;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.commons.lang3.StringUtils;
030
031import org.ametys.core.schedule.Runnable;
032import org.ametys.core.schedule.RunnableExtensionPoint;
033import org.ametys.core.schedule.Schedulable;
034import org.ametys.core.schedule.SchedulableExtensionPoint;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.core.user.population.UserPopulationDAO;
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.runtime.model.ElementDefinition;
039import org.ametys.runtime.model.ModelHelper;
040import org.ametys.runtime.plugin.component.PluginAware;
041
042/**
043 * Static implementation of {@link Runnable} which is configurable
044 */
045public class StaticRunnable implements Runnable, Component, Configurable, PluginAware, Serviceable
046{
047    /** The extension point for {@link Schedulable}s */
048    protected SchedulableExtensionPoint _schedulableEP;
049    /** The name of the plugin that declared this component */
050    protected String _pluginName;
051    /** The name of the feature that declared this component */
052    protected String _featureName;
053    /** The id of this extension */
054    protected String _id;
055    /** The label */
056    protected I18nizableText _label;
057    /** The description */
058    protected I18nizableText _description;
059    /** The fire process */
060    protected FireProcess _fireProcess;
061    /** The CRON expression for scheduling the job */
062    protected String _cronExpression;
063    /** The id of the {@link Schedulable} to execute */
064    protected String _schedulableId;
065    /** Can the runnable be removed */
066    protected boolean _removable;
067    /** Can the runnable be edited */
068    protected boolean _modifiable;
069    /** Can the runnable be deactivated */
070    protected boolean _deactivatable;
071    /** The misfire policy. Default to MisfirePolicy#DO_NOTHING */
072    protected MisfirePolicy _misfirePolicy;
073    /** The parameter values */
074    protected Map<String, Object> _parameterValues;
075    
076    @Override
077    public void service(ServiceManager manager) throws ServiceException
078    {
079        _schedulableEP = (SchedulableExtensionPoint) manager.lookup(SchedulableExtensionPoint.ROLE);
080    }
081    
082    @Override
083    public void setPluginInfo(String pluginName, String featureName, String id)
084    {
085        _pluginName = pluginName;
086        _featureName = featureName;
087        _id = id;
088    }
089
090    @Override
091    public void configure(Configuration configuration) throws ConfigurationException
092    {
093        _label = I18nizableText.parseI18nizableText(configuration.getChild("label"), "plugin." + _pluginName);
094        _description = I18nizableText.parseI18nizableText(configuration.getChild("description"), "plugin." + _pluginName);
095        _fireProcess = FireProcess.valueOf(configuration.getChild("fire-process").getValue("cron").toUpperCase());
096        _cronExpression = configuration.getChild("cron").getValue("0 0 2 * * ? *");
097        _schedulableId = configuration.getChild("schedulableId").getValue();
098        if (!_schedulableEP.hasExtension(_schedulableId))
099        {
100            String message = String.format("The extension '%s' of point '%s' declared in the feature '%s' in the plugin '%s' references the Schedulable extension '%s' but it seems to not exist.", 
101                    _id, RunnableExtensionPoint.class.getName(), _featureName, _pluginName, _schedulableId);
102            throw new ConfigurationException(message, configuration);
103        }
104        
105        _removable = configuration.getChild("removable").getValueAsBoolean(false);
106        _modifiable = configuration.getChild("modifiable").getValueAsBoolean(false);
107        _deactivatable = configuration.getChild("deactivatable").getValueAsBoolean(false);
108        _misfirePolicy = MisfirePolicy.valueOf(configuration.getChild("misfire-policy").getValue("do_nothing").toUpperCase());
109        _configureParameterValues(configuration.getChild("parameters"));
110    }
111
112    @Override
113    public String getId()
114    {
115        return _id;
116    }
117
118    @Override
119    public I18nizableText getLabel()
120    {
121        return _label;
122    }
123
124    @Override
125    public I18nizableText getDescription()
126    {
127        return _description;
128    }
129    
130    @Override
131    public FireProcess getFireProcess()
132    {
133        return _fireProcess;
134    }
135    
136    @Override
137    public String getCronExpression()
138    {
139        return _cronExpression;
140    }
141
142    @Override
143    public String getSchedulableId()
144    {
145        return _schedulableId;
146    }
147
148    @Override
149    public boolean isRemovable()
150    {
151        return _removable;
152    }
153
154    @Override
155    public boolean isModifiable()
156    {
157        return _modifiable;
158    }
159
160    @Override
161    public boolean isDeactivatable()
162    {
163        return _deactivatable;
164    }
165
166    @Override
167    public MisfirePolicy getMisfirePolicy()
168    {
169        return _misfirePolicy;
170    }
171    
172    @Override
173    public boolean isVolatile()
174    {
175        // A configurable runnable is read every time the server restart, so it must be volatile
176        return true;
177    }
178    
179    @Override
180    public Map<String, Object> getParameterValues()
181    {
182        return _parameterValues;
183    }
184    
185    /**
186     * Configure the values of the parameters
187     * @param paramConfigs the configurations of the parameter values
188     * @throws ConfigurationException if an error occurs
189     */
190    protected void _configureParameterValues(Configuration paramConfigs) throws ConfigurationException
191    {
192        _parameterValues = new HashMap<>();
193        
194        Schedulable schedulable = _schedulableEP.getExtension(_schedulableId);
195        Map<String, ElementDefinition> declaredParameters = schedulable.getParameters();
196        
197        for (String paramId : declaredParameters.keySet())
198        {
199            ElementDefinition definition = declaredParameters.get(paramId);
200            Object typedValue = null;
201
202            Configuration paramConf = paramConfigs.getChild(paramId, false);
203            if (paramConf != null)
204            {
205                String valueAsString = paramConf.getValue(StringUtils.EMPTY);
206                typedValue = definition.getType().castValue(valueAsString);
207            }
208            
209            List<I18nizableText> errors = ModelHelper.validateValue(definition, typedValue);
210            if (errors.isEmpty())
211            {
212                _parameterValues.put(paramId, typedValue);
213            }
214            else
215            {
216                StringBuilder message = new StringBuilder("The parameter '").append(paramId).append("' is not valid for the Runnable of id '").append(_id).append("':");
217                for (I18nizableText error : errors)
218                {
219                    message.append("\n* " + error.toString());
220                }
221                
222                throw new ConfigurationException(message.toString(), paramConfigs);
223            }
224        }
225    }
226
227    @Override
228    public UserIdentity getUserIdentity()
229    {
230        return UserPopulationDAO.SYSTEM_USER_IDENTITY;
231    }
232}