001/*
002 *  Copyright 2017 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.extraction.execution;
017
018import java.io.File;
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.LinkedHashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Map.Entry;
026
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.commons.lang.StringUtils;
030import org.apache.excalibur.source.Source;
031import org.apache.excalibur.source.SourceResolver;
032import org.apache.excalibur.source.impl.FileSource;
033import org.quartz.JobKey;
034import org.quartz.SchedulerException;
035
036import org.ametys.core.schedule.Runnable;
037import org.ametys.core.ui.Callable;
038import org.ametys.core.ui.StaticClientSideElement;
039import org.ametys.core.user.User;
040import org.ametys.core.user.UserIdentity;
041import org.ametys.core.user.UserManager;
042import org.ametys.core.util.JSONUtils;
043import org.ametys.plugins.core.schedule.Scheduler;
044import org.ametys.plugins.extraction.ExtractionConstants;
045import org.ametys.plugins.extraction.execution.pipeline.PipelineManager;
046import org.ametys.runtime.i18n.I18nizableText;
047
048/**
049 * This client site element creates a button to execute an extraction
050 */
051public class ExecuteExtractionClientSideElement extends StaticClientSideElement
052{
053    private UserManager _userManager;
054    private ExtractionDefinitionReader _reader;
055    private SourceResolver _sourceResolver;
056    private JSONUtils _jsonUtils;
057    private Scheduler _scheduler;
058    private PipelineManager _pipelineManager;
059    
060    @Override
061    public void service(ServiceManager serviceManager) throws ServiceException
062    {
063        super.service(serviceManager);
064        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
065        _reader = (ExtractionDefinitionReader) serviceManager.lookup(ExtractionDefinitionReader.ROLE);
066        _sourceResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
067        _jsonUtils = (JSONUtils) serviceManager.lookup(JSONUtils.ROLE);
068        _scheduler = (Scheduler) serviceManager.lookup(Scheduler.ROLE);
069        _pipelineManager = (PipelineManager) serviceManager.lookup(PipelineManager.ROLE);
070    }
071    
072    /**
073     * Retrieve needed extraction parameters.
074     * @param definitionFile The extraction definition file path
075     * @return a <code>Map</code> containing parameters infos used to configure form to fill the parameters
076     * @throws Exception if an error occurs
077     */
078    @Callable (right = "Extraction_Rights_ExecuteExtraction")
079    public Map<String, Object> getExecutionParameters(String definitionFile) throws Exception
080    {
081        Map<String, Object> executionParameters = new LinkedHashMap<>();
082
083        String definitionFilePath = ExtractionConstants.DEFINITIONS_DIR + definitionFile;
084        Source src = _sourceResolver.resolveURI(definitionFilePath);
085        File file = ((FileSource) src).getFile();
086        
087        if (!file.exists())
088        {
089            throw new IllegalArgumentException("The file " + definitionFilePath + " does not exist.");
090        }
091
092        executionParameters.put("pipeline", _getPipelineInputConfig());
093        
094        Extraction extraction = _reader.readExtractionDefinitionFile(file);
095        
096        Map<String, String> clasuesVariables = extraction.getQueryVariablesNamesAndContentTypes();
097        List<String> optionalColumns = extraction.getDisplayOptionalColumnsNames();
098        if (!clasuesVariables.isEmpty() || !optionalColumns.isEmpty())
099        {
100            List<Map<String, Object>> variablesFieldSets = new ArrayList<>();
101            if (!clasuesVariables.isEmpty())
102            {
103                Map<String, Object> clausesVariablesFieldSet = new HashMap<>();
104                clausesVariablesFieldSet.put("role", "fieldset");
105                clausesVariablesFieldSet.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_CLAUSES_VARIABLES_FIELDSET_LABEL"));
106            
107                Map<String, Object> clausesVariablesFieldSetElements = new HashMap<>();
108                for (Map.Entry<String, String> clausesVariable : clasuesVariables.entrySet())
109                {
110                    clausesVariablesFieldSetElements.put(clausesVariable.getKey(), _getClauseVariableInputConfig(clausesVariable));
111                }
112                clausesVariablesFieldSet.put("elements", clausesVariablesFieldSetElements);
113                
114                variablesFieldSets.add(clausesVariablesFieldSet);
115            }
116            
117            if (!optionalColumns.isEmpty())
118            {
119                Map<String, Object> optionalColumnsFieldSet = new HashMap<>();
120                optionalColumnsFieldSet.put("role", "fieldset");
121                optionalColumnsFieldSet.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_OPTIONAL_COLUMNS_FIELDSET_LABEL"));
122            
123                Map<String, Object> optionalColumnsFieldSetElements = new HashMap<>();
124                for (String optionalColumn : optionalColumns)
125                {
126                    optionalColumnsFieldSetElements.put(optionalColumn, _getOptionalColumnsInputConfig(optionalColumn));
127                }
128                optionalColumnsFieldSet.put("elements", optionalColumnsFieldSetElements);
129                
130                variablesFieldSets.add(optionalColumnsFieldSet);
131            }
132            
133            executionParameters.put("fieldsets", variablesFieldSets);
134        }
135
136        executionParameters.put("recipient", _getRecipientInputConfig());
137        
138        return executionParameters;
139    }
140    
141    private Map<String, Object> _getPipelineInputConfig() throws IOException
142    {
143        Map<String, Object> inputConfig = new HashMap<>();
144        
145        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_PIPELINE_INPUT_LABEL"));
146        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_PIPELINE_INPUT_DESCRIPTION"));
147        inputConfig.put("type", "string");
148        
149        List<Map<String, Object>> enumeration = _pipelineManager.getJsonEnumeration();
150        
151        inputConfig.put("enumeration", enumeration);
152        inputConfig.put("default-value", _pipelineManager.getDefaultPipeline());
153        inputConfig.put("validation", _getMandatoryValidation());
154        
155        return inputConfig;
156    }
157    
158    private Map<String, Object> _getClauseVariableInputConfig(Entry<String, String> clausesVariable)
159    {
160        Map<String, Object> inputConfig = new HashMap<>();
161        
162        inputConfig.put("label", clausesVariable.getKey());
163        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_CLAUSES_VARIABLE_INPUTS_DESCRIPTION"));
164        
165        inputConfig.put("type", "content");
166        inputConfig.put("widget", "edition.select-content");
167        
168        Map<String, Object> widgetParams = new HashMap<>();
169        widgetParams.put("contentType", clausesVariable.getValue());
170        inputConfig.put("widget-params", widgetParams);
171        
172        inputConfig.put("validation", _getMandatoryValidation());
173        
174        return inputConfig;
175    }
176    
177    private Map<String, Object> _getOptionalColumnsInputConfig(String optionalColumn)
178    {
179        Map<String, Object> inputConfig = new HashMap<>();
180        
181        inputConfig.put("label", optionalColumn);
182        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_OPTIONAL_COLUMNS_INPUTS_DESCRIPTION"));
183        
184        inputConfig.put("type", "boolean");
185        inputConfig.put("widget", "edition.checkbox");
186        
187        inputConfig.put("validation", _getMandatoryValidation());
188        
189        return inputConfig;
190    }
191    
192    private Map<String, Object> _getRecipientInputConfig()
193    {
194        Map<String, Object> inputConfig = new HashMap<>();
195        
196        // Get current user email
197        String currentUserEmail = null;
198        UserIdentity currentUser = _currentUserProvider.getUser();
199        
200        String login = currentUser.getLogin();
201        if (StringUtils.isNotBlank(login))
202        {
203            String userPopulationId = currentUser.getPopulationId();
204            User user = _userManager.getUser(userPopulationId, login);
205            currentUserEmail = user.getEmail();
206        }
207        
208        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_LABEL"));
209        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_DESCRIPTION"));
210        inputConfig.put("type", "string");
211        inputConfig.put("default-value", currentUserEmail);
212        
213        return inputConfig;
214    }
215    
216    private Map<String, Object> _getMandatoryValidation()
217    {
218        Map<String, Object> mandatoryValidation = new HashMap<>();
219        mandatoryValidation.put("mandatory", true);
220        return mandatoryValidation;
221    }
222    
223    /**
224     * Execute the extraction
225     * @param definitionFilePath The extraction definition file path
226     * @param variables clauses variables and optional columns
227     * @param recipient An email will be sent at this address when the extraction is complete
228     * @param pipelineId The id of the extraction pipeline
229     * @return a Map with error if one occurs
230     * @throws Exception if an error occurs
231     */
232    @Callable (right = "Extraction_Rights_ExecuteExtraction")
233    public Map<String, Object> executeExtraction(String definitionFilePath, Map<String, Object> variables, String recipient, String pipelineId) throws Exception
234    {
235        Map<String, Object> result = new HashMap<>();
236        
237        try
238        {
239            String variablesAsString = _jsonUtils.convertObjectToJson(variables);
240            Runnable executeExtractionRunnable = new ExecuteExtractionRunnable(definitionFilePath, variablesAsString, recipient, pipelineId);
241            JobKey jobKey = new JobKey(executeExtractionRunnable.getId(), Scheduler.JOB_GROUP);
242            if (_scheduler.getScheduler().checkExists(jobKey))
243            {
244                _scheduler.getScheduler().deleteJob(jobKey);
245            }
246            _scheduler.scheduleJob(executeExtractionRunnable);
247            getLogger().info("Scheduled extraction execution of " + definitionFilePath);
248        }
249        catch (SchedulerException e)
250        {
251            if (getLogger().isErrorEnabled())
252            {
253                getLogger().error("An error occured when trying to schedule the extraction execution of " + definitionFilePath, e);
254            }
255            result.put("error", "scheduler-error");
256            return result;
257        }
258        
259        result.put("definitionFilePath", definitionFilePath);
260        return result;
261    }
262}