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