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.LinkedHashMap;
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.context.Context;
026import org.apache.avalon.framework.context.ContextException;
027import org.apache.avalon.framework.context.Contextualizable;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.quartz.JobDataMap;
032import org.quartz.JobExecutionContext;
033
034import org.ametys.core.schedule.Schedulable;
035import org.ametys.core.schedule.SchedulableParameterTypeExtensionPoint;
036import org.ametys.core.user.User;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.user.UserManager;
039import org.ametys.plugins.core.schedule.Scheduler;
040import org.ametys.runtime.i18n.I18nizableText;
041import org.ametys.runtime.model.ElementDefinition;
042import org.ametys.runtime.model.ElementDefinitionParser;
043import org.ametys.runtime.model.Enumerator;
044import org.ametys.runtime.parameter.Validator;
045import org.ametys.runtime.plugin.component.AbstractLogEnabled;
046import org.ametys.runtime.plugin.component.PluginAware;
047import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
048
049/**
050 * Default static implementation of {@link Schedulable}
051 * For implementing the {@link Schedulable} interface (while being {@link Configurable}), extends this class and implements the {@link #execute(org.quartz.JobExecutionContext)} method
052 * <br>
053 * For instance:
054 * <pre>
055 * public class SayHelloSchedulable extends AbstractStaticSchedulable
056 * {
057 *     public static final String FIRSTNAME_KEY = "firstName";
058 *     
059 *     private static final String __JOBDATAMAP_FIRSTNAME_KEY = Scheduler.PARAM_VALUES_PREFIX + FIRSTNAME_KEY;
060 *     
061 *     public void execute(JobExecutionContext context) throws Exception
062 *     {
063 *         JobKey jobKey = context.getJobDetail().getKey();
064 *         JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
065 *         String name = jobDataMap.getString(__JOBDATAMAP_FIRSTNAME_KEY);
066 *         System.out.println("[" + jobKey + "] " + new Date() + " - Hello  " + name + "!");
067 *     }
068 * }
069 * </pre>
070 */
071public abstract class AbstractStaticSchedulable extends AbstractLogEnabled implements Schedulable, Component, Configurable, PluginAware, Serviceable, Contextualizable
072{
073    /** The name of the plugin that has declared this component */
074    protected String _pluginName;
075    /** The id of this extension */
076    protected String _id;
077    /** The service manager */
078    protected ServiceManager _smanager;
079    /** The context */
080    protected Context _context;
081    /** The label */
082    protected I18nizableText _label;
083    /** The description */
084    protected I18nizableText _description;
085    /** The icon glyph */
086    protected String _iconGlyph;
087    /** The small icon */
088    protected String _iconSmall;
089    /** The medium icon */
090    protected String _iconMedium;
091    /** The large icon */
092    protected String _iconLarge;
093    /** True if the schedulable is private */
094    protected boolean _private;
095    /** True if two runnables of this schedulable can be executed concurrently */
096    protected boolean _acceptConcurrentExecution;
097    /** The parameters */
098    protected Map<String, ElementDefinition> _parameters;
099    /** The Schedulable Extension Point */
100    protected SchedulableParameterTypeExtensionPoint _schedulableParameterTypeExtensionPoint;
101    /** The user manager */
102    protected UserManager _userManager;
103    
104    @Override
105    public void setPluginInfo(String pluginName, String featureName, String id)
106    {
107        _pluginName = pluginName;
108        _id = id;
109    }
110    
111    @Override
112    public void contextualize(Context context) throws ContextException
113    {
114        _context = context;
115    }
116    
117    @Override
118    public void service(ServiceManager manager) throws ServiceException
119    {
120        _smanager = manager;
121        _schedulableParameterTypeExtensionPoint = (SchedulableParameterTypeExtensionPoint) _smanager.lookup(SchedulableParameterTypeExtensionPoint.ROLE);
122        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
123    }
124    
125    @Override
126    public void configure(Configuration configuration) throws ConfigurationException
127    {
128        _label = I18nizableText.parseI18nizableText(configuration.getChild("label"), "plugin." + _pluginName);
129        _description = I18nizableText.parseI18nizableText(configuration.getChild("description"), "plugin." + _pluginName);
130        _iconGlyph = configuration.getChild("icon-glyph").getValue("");
131        _iconSmall = configuration.getChild("icon-small").getValue("");
132        _iconMedium = configuration.getChild("icon-medium").getValue("");
133        _iconLarge = configuration.getChild("icon-large").getValue("");
134        _private = configuration.getChild("private").getValueAsBoolean(false);
135        _acceptConcurrentExecution = configuration.getChild("acceptConcurrentExecution").getValueAsBoolean(true);
136        _configureParameters(configuration.getChild("parameters"));
137    }
138    
139    private void _configureParameters(Configuration paramConfigs) throws ConfigurationException
140    {
141        _parameters = new LinkedHashMap<>();
142        
143        ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>();
144        validatorManager.setLogger(getLogger());
145        validatorManager.contextualize(_context);
146        validatorManager.service(_smanager);
147        
148        ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>();
149        enumeratorManager.setLogger(getLogger());
150        enumeratorManager.contextualize(_context);
151        enumeratorManager.service(_smanager);
152        
153        SchedulableParameterParser paramParser = new SchedulableParameterParser(_schedulableParameterTypeExtensionPoint, enumeratorManager, validatorManager);
154        for (Configuration paramConf : paramConfigs.getChildren("param"))
155        {
156            ElementDefinition definition = paramParser.parse(_smanager, _pluginName, paramConf, null, null);
157            String id = definition.getName();
158            
159            if (_parameters.containsKey(id))
160            {
161                throw new ConfigurationException("The parameter '" + id + "' is already declared. IDs must be unique.", paramConf);
162            }
163            
164            _parameters.put(id, definition);
165        }
166        
167        try
168        {
169            paramParser.lookupComponents();
170        }
171        catch (Exception e)
172        {
173            throw new ConfigurationException("Unable to lookup parameter local components", paramConfigs, e);
174        }
175    }
176    
177    @Override
178    public abstract void execute(JobExecutionContext context) throws Exception;
179    
180    /**
181     * Retrieves the user who launched the schedulable
182     * @param context the schedulable's execution context
183     * @return the user who launched the schedulable
184     */
185    protected User _getLaunchUser(JobExecutionContext context)
186    {
187        UserIdentity launchUserIdentity = _getLaunchUserIdentity(context);
188        return _userManager.getUser(launchUserIdentity);
189    }
190    
191    /**
192     * Retrieves the identity of the user who launched the schedulable
193     * @param context the schedulable's execution context
194     * @return the identity of the user who launched the schedulable
195     */
196    protected UserIdentity _getLaunchUserIdentity(JobExecutionContext context)
197    {
198        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
199        return UserIdentity.stringToUserIdentity(jobDataMap.getString(Scheduler.KEY_RUNNABLE_USERIDENTITY));
200    }
201    
202    @Override
203    public String getId()
204    {
205        return _id;
206    }
207
208    @Override
209    public I18nizableText getLabel()
210    {
211        return _label;
212    }
213
214    @Override
215    public I18nizableText getDescription()
216    {
217        return _description;
218    }
219
220    @Override
221    public String getIconGlyph()
222    {
223        return _iconGlyph;
224    }
225
226    @Override
227    public String getIconSmall()
228    {
229        return _iconSmall;
230    }
231
232    @Override
233    public String getIconMedium()
234    {
235        return _iconMedium;
236    }
237
238    @Override
239    public String getIconLarge()
240    {
241        return _iconLarge;
242    }
243    
244    @Override
245    public boolean isPrivate()
246    {
247        return _private;
248    }
249    
250    @Override
251    public boolean acceptConcurrentExecution()
252    {
253        return _acceptConcurrentExecution;
254    }
255
256    @Override
257    public Map<String, ElementDefinition> getParameters()
258    {
259        return _parameters;
260    }
261    
262    /**
263     * Class for parsing parameters of a {@link Schedulable}
264     */
265    public class SchedulableParameterParser extends ElementDefinitionParser
266    {
267        /**
268         * Constructor
269         * @param schedulableParameterTypeExtensionPoint the extension point to use to get available element types
270         * @param enumeratorManager The manager for enumeration
271         * @param validatorManager The manager for validation
272         */
273        public SchedulableParameterParser(SchedulableParameterTypeExtensionPoint schedulableParameterTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager)
274        {
275            super(schedulableParameterTypeExtensionPoint, enumeratorManager, validatorManager);
276        }
277        
278        @Override
279        protected String _getNameConfigurationAttribute()
280        {
281            return "id";
282        }
283    }
284}