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.nio.file.Path;
020import java.time.ZonedDateTime;
021import java.time.format.DateTimeFormatter;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Optional;
025import java.util.Set;
026import java.util.regex.Pattern;
027
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.commons.lang3.StringUtils;
031import org.apache.commons.lang3.exception.ExceptionUtils;
032import org.quartz.JobDataMap;
033import org.quartz.JobExecutionContext;
034
035import org.ametys.cms.schedule.AbstractSendingMailSchedulable;
036import org.ametys.core.schedule.Schedulable;
037import org.ametys.core.util.I18nUtils;
038import org.ametys.core.util.JSONUtils;
039import org.ametys.plugins.core.schedule.Scheduler;
040import org.ametys.plugins.extraction.ExtractionConstants;
041import org.ametys.plugins.extraction.execution.pipeline.PipelineDescriptor;
042import org.ametys.plugins.extraction.execution.pipeline.PipelineManager;
043import org.ametys.runtime.config.Config;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.runtime.i18n.I18nizableTextParameter;
046import org.ametys.runtime.util.AmetysHomeHelper;
047
048/**
049 * A {@link Schedulable} job which execute an extraction
050 */
051public class ExecuteExtractionSchedulable extends AbstractSendingMailSchedulable
052{
053    /** The key for the extraction definition file */
054    public static final String DEFINITION_FILE_PATH_KEY = "definitionFilePath";
055    /** The key for the variables values */
056    public static final String VARIABLES_KEY = "variables";
057    /** The key for the recipient */
058    public static final String RECIPIENT_KEY = "recipient";
059    /** The key for the pipeline */
060    public static final String PIPELINE_KEY = "pipeline";
061    
062    private static final String __JOBDATAMAP_DEFINITION_FILE_PATH_KEY = Scheduler.PARAM_VALUES_PREFIX + DEFINITION_FILE_PATH_KEY;
063    private static final String __JOBDATAMAP_VARIABLES_KEY = Scheduler.PARAM_VALUES_PREFIX + VARIABLES_KEY;
064    private static final String __JOBDATAMAP_RECIPIENT_KEY = Scheduler.PARAM_VALUES_PREFIX + RECIPIENT_KEY;
065    private static final String __JOBDATAMAP_PIPELINE_KEY = Scheduler.PARAM_VALUES_PREFIX + PIPELINE_KEY;
066
067    private static final String __RESULT_FILE_PATHS = "resultFilePaths";
068    
069    private static final DateTimeFormatter RESULT_FILE_NAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd'T'HH.mm.ss");
070    
071    private JSONUtils _jsonUtils;
072    private PipelineManager _pipelineManager;
073    private ExtractionExecutor _extractionExecutor;
074    
075    @Override
076    public void service(ServiceManager manager) throws ServiceException
077    {
078        super.service(manager);
079        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
080        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
081        _pipelineManager = (PipelineManager) manager.lookup(PipelineManager.ROLE);
082        _extractionExecutor = (ExtractionExecutor) manager.lookup(ExtractionExecutor.ROLE);
083    }
084    
085    @Override
086    public void _doExecute(JobExecutionContext context) throws Exception
087    {
088        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
089
090        String definitionFilePath = _getDefinitionFilePath(context);
091        PipelineDescriptor pipeline = _pipelineManager.get(jobDataMap.getString(__JOBDATAMAP_PIPELINE_KEY));
092        String defaultResultFileName = _getDefaultResultFileName(definitionFilePath, pipeline);
093
094        Map<String, Object> parameters = _getExtractionParameters(jobDataMap);
095        String lang = (String) parameters.get("lang");
096        
097        Set<Path> resultFilePaths = _extractionExecutor.execute(definitionFilePath, defaultResultFileName, lang, parameters, pipeline);
098        context.put(__RESULT_FILE_PATHS, resultFilePaths);
099    }
100
101    private String _getDefaultResultFileName(String definitionFilePath, PipelineDescriptor pipeline)
102    {
103        String[] definitionFilePathSegments = definitionFilePath.split(Pattern.quote(File.separator));
104        String definitionFileName = definitionFilePathSegments[definitionFilePathSegments.length - 1];
105        
106        int lastIndexOfDot = definitionFileName.lastIndexOf('.');
107        if (-1 != lastIndexOfDot)
108        {
109            definitionFileName = definitionFileName.substring(0, lastIndexOfDot);
110        }
111        
112        String extractionDate = ZonedDateTime.now().format(RESULT_FILE_NAME_DATE_TIME_FORMATTER);
113        
114        StringBuilder resultFileName = new StringBuilder();
115        resultFileName.append(definitionFileName).append("-").append(extractionDate);
116        resultFileName.append(".").append(pipeline.getDefaultExtension());
117        return resultFileName.toString();
118    }
119    
120    private Map<String, Object> _getExtractionParameters(JobDataMap jobDataMap)
121    {
122        // variables parameters
123        String variablesAsString = jobDataMap.getString(__JOBDATAMAP_VARIABLES_KEY);
124        Map<String, Object> variablesAsMap = _jsonUtils.convertJsonToMap(variablesAsString);
125        return variablesAsMap;
126    }
127    
128    @Override
129    protected Optional<String> _getRecipient(JobExecutionContext context)
130    {
131        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
132        return Optional.ofNullable(jobDataMap.getString(__JOBDATAMAP_RECIPIENT_KEY))
133                .filter(StringUtils::isNotEmpty);
134    }
135    
136    @Override
137    protected boolean _isMailBodyInHTML(JobExecutionContext context)
138    {
139        return true;
140    }
141    
142    @Override
143    protected I18nizableText _getSuccessMailSubject(JobExecutionContext context)
144    {
145        String extractionName = _getDefinitionFilePath(context);
146        return new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_SUCCESS_SUBJECT", Map.of("extractionName", new I18nizableText(extractionName)));
147    }
148    
149    @Override
150    protected I18nizableText _getSuccessMailBody(JobExecutionContext context)
151    {
152        Map<String, I18nizableTextParameter> params = new HashMap<>();
153        
154        String extractionName = _getDefinitionFilePath(context);
155        params.put("extractionName", new I18nizableText(extractionName));
156        
157        I18nizableText resultsToolLabel = new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_RESULTS_LIST_TOOL_LABEL");
158        params.put("toolLabel", resultsToolLabel);
159
160        @SuppressWarnings("unchecked")
161        Set<Path> resultFilePaths = (Set<Path>) context.get(__RESULT_FILE_PATHS);
162        if (resultFilePaths.size() == 1)
163        {
164            String downloadLink = _getResultFileDownloadLink(resultFilePaths.iterator().next());
165            
166            params.put("link", new I18nizableText(downloadLink));
167            return new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_SUCCESS_BODY_ONE_RESULT", params);
168        }
169        else
170        {
171            StringBuilder downloadLinks = new StringBuilder();
172            for (Path resultFilePath : resultFilePaths)
173            {
174                downloadLinks.append("<li>")
175                             .append(_getResultFileDownloadLink(resultFilePath))
176                             .append("</li>");
177            }
178            
179            params.put("links", new I18nizableText(downloadLinks.toString()));
180            return new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_SUCCESS_BODY_SEVERAL_RESULTS", params);
181        }
182    }
183    
184    @Override
185    protected I18nizableText _getErrorMailSubject(JobExecutionContext context)
186    {
187        String extractionName = _getDefinitionFilePath(context);
188        return new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_ERROR_SUBJECT", Map.of("extractionName", new I18nizableText(extractionName)));
189    }
190    
191    @Override
192    protected I18nizableText _getErrorMailBody(JobExecutionContext context, Throwable throwable)
193    {
194        String extractionName = _getDefinitionFilePath(context);
195        String error = ExceptionUtils.getStackTrace(throwable);
196        
197        Map<String, I18nizableTextParameter> params = Map.of(
198                "extractionName", new I18nizableText(extractionName),
199                "error", new I18nizableText(error)
200                );
201        
202        return new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_ERROR_BODY", params);
203    }
204    
205    private String _getDefinitionFilePath(JobExecutionContext context)
206    {
207        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
208        return jobDataMap.getString(__JOBDATAMAP_DEFINITION_FILE_PATH_KEY);
209    }
210    
211    private String _getResultFileDownloadLink(Path resultFilePath)
212    {
213        Path rootPath = AmetysHomeHelper.getAmetysHomeData().toPath().resolve(ExtractionConstants.RESULT_EXTRACTION_DIR_NAME);
214        
215        String rootURI = rootPath.toUri().toString();
216        String resultFileURI = resultFilePath.toUri().toString();
217        
218        String resultFileRelativeURI = resultFileURI.substring(rootURI.length());
219        String downloadURL = Config.getInstance().getValue("cms.url") + "/plugins/extraction/result/download/" + resultFileRelativeURI;
220
221        String resultFileRelativePath = rootPath.relativize(resultFilePath).toString();
222
223        return "<a href=\"" + downloadURL + "\">" + resultFileRelativePath + "</a>";
224    }
225    
226}