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.util.HashMap;
020import java.util.LinkedHashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Map.Entry;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.commons.lang.StringUtils;
028import org.apache.excalibur.source.Source;
029import org.apache.excalibur.source.SourceResolver;
030import org.apache.excalibur.source.impl.FileSource;
031import org.quartz.JobKey;
032import org.quartz.SchedulerException;
033
034import org.ametys.cms.contenttype.ContentType;
035import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
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    private ContentTypeExtensionPoint _contentTypeExtensionPoint;
060    
061    @Override
062    public void service(ServiceManager serviceManager) throws ServiceException
063    {
064        super.service(serviceManager);
065        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
066        _reader = (ExtractionDefinitionReader) serviceManager.lookup(ExtractionDefinitionReader.ROLE);
067        _sourceResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
068        _jsonUtils = (JSONUtils) serviceManager.lookup(JSONUtils.ROLE);
069        _scheduler = (Scheduler) serviceManager.lookup(Scheduler.ROLE);
070        _pipelineManager = (PipelineManager) serviceManager.lookup(PipelineManager.ROLE);
071        _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
072    }
073    
074    /**
075     * Retrieve needed extraction parameters.
076     * @param definitionFile The extraction definition file path
077     * @return a <code>Map</code> containing parameters infos used to configure form to fill the parameters
078     * @throws Exception if an error occurs
079     */
080    @Callable (right = ExtractionConstants.EXECUTE_EXTRACTION_RIGHT_ID)
081    public Map<String, Object> getExecutionParameters(String definitionFile) throws Exception
082    {
083        Map<String, Object> executionParameters = new LinkedHashMap<>();
084
085        String definitionFilePath = ExtractionConstants.DEFINITIONS_DIR + definitionFile;
086        Source src = _sourceResolver.resolveURI(definitionFilePath);
087        File file = ((FileSource) src).getFile();
088        
089        if (!file.exists())
090        {
091            throw new IllegalArgumentException("The file " + definitionFilePath + " does not exist.");
092        }
093
094        executionParameters.put("pipeline", _getPipelineInputConfig(definitionFile));
095        
096        Extraction extraction = _reader.readExtractionDefinitionFile(file);
097        
098        Map<String, String> clasuesVariables = extraction.getQueryVariablesNamesAndContentTypes();
099        List<String> optionalColumns = extraction.getDisplayOptionalColumnsNames();
100
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            executionParameters.put("clausesVariables", 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            executionParameters.put("optionalColumns", optionalColumnsFieldSet);
131        }
132            
133        executionParameters.put("recipient", _getRecipientInputConfig());
134        
135        return executionParameters;
136    }
137    
138    private Map<String, Object> _getPipelineInputConfig(String definitionFile)
139    {
140        Map<String, Object> inputConfig = new HashMap<>();
141        
142        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_PIPELINE_INPUT_LABEL"));
143        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_PIPELINE_INPUT_DESCRIPTION"));
144        inputConfig.put("type", "string");
145        
146        inputConfig.put("default-value", _pipelineManager.getDefaultPipeline());
147        inputConfig.put("validation", _getMandatoryValidation());
148        
149        inputConfig.put("widget", "edition.select-pipeline");
150        
151        Map<String, Object> widgetParams = new HashMap<>();
152        widgetParams.put("extraction", definitionFile);
153        inputConfig.put("widget-params", widgetParams);
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("multiple", true);
167        
168        String contentTypeId = clausesVariable.getValue();
169        ContentType contentType = _contentTypeExtensionPoint.getExtension(contentTypeId);
170        
171        String widget = contentType.isReferenceTable() ? "edition.select-referencetable-content" : "edition.select-content";
172        inputConfig.put("widget", widget);
173        
174        Map<String, Object> widgetParams = new HashMap<>();
175        widgetParams.put("contentType", contentTypeId);
176        inputConfig.put("widget-params", widgetParams);
177        
178        inputConfig.put("validation", _getMandatoryValidation());
179        
180        return inputConfig;
181    }
182    
183    private Map<String, Object> _getOptionalColumnsInputConfig(String optionalColumn)
184    {
185        Map<String, Object> inputConfig = new HashMap<>();
186        
187        inputConfig.put("label", optionalColumn);
188        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_OPTIONAL_COLUMNS_INPUTS_DESCRIPTION"));
189        
190        inputConfig.put("type", "boolean");
191        inputConfig.put("widget", "edition.checkbox");
192        
193        inputConfig.put("validation", _getMandatoryValidation());
194        
195        return inputConfig;
196    }
197    
198    private Map<String, Object> _getRecipientInputConfig()
199    {
200        Map<String, Object> inputConfig = new HashMap<>();
201        
202        // Get current user email
203        String currentUserEmail = null;
204        UserIdentity currentUser = _currentUserProvider.getUser();
205        
206        String login = currentUser.getLogin();
207        if (StringUtils.isNotBlank(login))
208        {
209            String userPopulationId = currentUser.getPopulationId();
210            User user = _userManager.getUser(userPopulationId, login);
211            currentUserEmail = user.getEmail();
212        }
213        
214        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_LABEL"));
215        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_DESCRIPTION"));
216        inputConfig.put("type", "string");
217        inputConfig.put("default-value", currentUserEmail);
218        
219        return inputConfig;
220    }
221    
222    private Map<String, Object> _getMandatoryValidation()
223    {
224        Map<String, Object> mandatoryValidation = new HashMap<>();
225        mandatoryValidation.put("mandatory", true);
226        return mandatoryValidation;
227    }
228    
229    /**
230     * Execute the extraction
231     * @param definitionFilePath The extraction definition file path
232     * @param variables clauses variables and optional columns
233     * @param recipient An email will be sent at this address when the extraction is complete
234     * @param pipelineId The id of the extraction pipeline
235     * @return a Map with error if one occurs
236     * @throws Exception if an error occurs
237     */
238    @Callable (right = ExtractionConstants.EXECUTE_EXTRACTION_RIGHT_ID)
239    public Map<String, Object> executeExtraction(String definitionFilePath, Map<String, Object> variables, String recipient, String pipelineId) throws Exception
240    {
241        Map<String, Object> result = new HashMap<>();
242        
243        try
244        {
245            String variablesAsString = _jsonUtils.convertObjectToJson(variables);
246            Runnable executeExtractionRunnable = new ExecuteExtractionRunnable(definitionFilePath, variablesAsString, recipient, pipelineId);
247            JobKey jobKey = new JobKey(executeExtractionRunnable.getId(), Scheduler.JOB_GROUP);
248            if (_scheduler.getScheduler().checkExists(jobKey))
249            {
250                _scheduler.getScheduler().deleteJob(jobKey);
251            }
252            _scheduler.scheduleJob(executeExtractionRunnable);
253            getLogger().info("Scheduled extraction execution of " + definitionFilePath);
254        }
255        catch (SchedulerException e)
256        {
257            if (getLogger().isErrorEnabled())
258            {
259                getLogger().error("An error occured when trying to schedule the extraction execution of " + definitionFilePath, e);
260            }
261            result.put("error", "scheduler-error");
262            return result;
263        }
264        
265        result.put("definitionFilePath", definitionFilePath);
266        return result;
267    }
268}