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.InputStream; 020import java.io.OutputStream; 021import java.nio.file.Files; 022import java.nio.file.Paths; 023import java.time.ZonedDateTime; 024import java.time.format.DateTimeFormatter; 025import java.util.Collections; 026import java.util.Map; 027import java.util.regex.Pattern; 028 029import javax.mail.MessagingException; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.components.source.impl.SitemapSource; 034import org.apache.commons.lang.StringUtils; 035import org.apache.excalibur.source.SourceResolver; 036import org.apache.excalibur.source.SourceUtil; 037import org.quartz.JobDataMap; 038import org.quartz.JobExecutionContext; 039 040import org.ametys.core.schedule.Schedulable; 041import org.ametys.core.util.I18nUtils; 042import org.ametys.core.util.JSONUtils; 043import org.ametys.core.util.URLEncoder; 044import org.ametys.core.util.mail.SendMailHelper; 045import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable; 046import org.ametys.plugins.core.schedule.Scheduler; 047import org.ametys.plugins.extraction.ExtractionConstants; 048import org.ametys.runtime.config.Config; 049import org.ametys.runtime.i18n.I18nizableText; 050import org.ametys.runtime.util.AmetysHomeHelper; 051 052/** 053 * A {@link Schedulable} job which execute an extraction 054 */ 055public class ExecuteExtractionSchedulable extends AbstractStaticSchedulable 056{ 057 /** The key for the extraction definition file */ 058 public static final String DEFINITION_FILE_PATH_KEY = "definitionFilePath"; 059 /** The key for the variables values */ 060 public static final String VARIABLES_KEY = "variables"; 061 /** The key for the recipient */ 062 public static final String RECIPIENT_KEY = "recipient"; 063 /** The key for the format */ 064 public static final String FORMAT_KEY = "format"; 065 /** The key for the stylesheet */ 066 public static final String XSLT_FILE_PATH_KEY = "xsltFilePath"; 067 068 private static final String EXTRACTION_DIR_NAME = "extraction"; 069 private static final String PDF_FORMAT_PARAM_VALUE = "pdf"; 070 private static final String XML_FORMAT_SUFFIX = "xml"; 071 private static final String PDF_FORMAT_SUFFIX = "pdf"; 072 073 private static final DateTimeFormatter RESULT_FILE_NAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd'T'HH.mm.ss"); 074 075 private JSONUtils _jsonUtils; 076 private SourceResolver _sourceResolver; 077 private I18nUtils _i18nUtils; 078 private String _mailFrom; 079 080 @Override 081 public void service(ServiceManager manager) throws ServiceException 082 { 083 super.service(manager); 084 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 085 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 086 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 087 _mailFrom = Config.getInstance().getValueAsString("smtp.mail.from"); 088 } 089 090 @Override 091 public void execute(JobExecutionContext context) throws Exception 092 { 093 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); 094 String definitionFilePath = (String) jobDataMap.get(Scheduler.PARAM_VALUES_PREFIX + DEFINITION_FILE_PATH_KEY); 095 096 // Get email address to send one when the extraction is complete (or if there is an error) 097 String recipient = (String) jobDataMap.get(Scheduler.PARAM_VALUES_PREFIX + RECIPIENT_KEY); 098 099 SitemapSource source = null; 100 File resultFile = null; 101 String mailBody = null; 102 try 103 { 104 StringBuilder uri = new StringBuilder(); 105 uri.append("cocoon://_admin/plugins/extraction/extract."); 106 107 String format = (String) jobDataMap.get(Scheduler.PARAM_VALUES_PREFIX + FORMAT_KEY); 108 uri.append(PDF_FORMAT_PARAM_VALUE.equals(format) ? PDF_FORMAT_SUFFIX : XML_FORMAT_SUFFIX); 109 110 String parameters = _getExtractionParameters(jobDataMap, definitionFilePath); 111 uri.append("?").append(parameters); 112 113 // Execute the extraction 114 source = (SitemapSource) _sourceResolver.resolveURI(uri.toString()); 115 116 // Create extraction results directory 117 File extractionsDir = new File(AmetysHomeHelper.getAmetysHomeData(), EXTRACTION_DIR_NAME); 118 extractionsDir.mkdirs(); 119 120 // Save result into a file. 121 String resultFileName = _getResultFileName(definitionFilePath, format); 122 resultFile = new File(extractionsDir, resultFileName); 123 try (OutputStream resultOs = Files.newOutputStream(Paths.get(resultFile.getAbsolutePath())); InputStream sourceIs = source.getInputStream()) 124 { 125 SourceUtil.copy(sourceIs, resultOs); 126 } 127 128 mailBody = _getSuccessMailBody(definitionFilePath); 129 } 130 catch (Exception e) 131 { 132 mailBody = _getFailureMailBody(definitionFilePath, e.getMessage()); 133 throw e; 134 } 135 finally 136 { 137 if (source != null) 138 { 139 _sourceResolver.release(source); 140 } 141 142 if (!StringUtils.isEmpty(recipient)) 143 { 144 _sendMail(recipient, mailBody); 145 } 146 } 147 } 148 149 private String _getExtractionParameters(JobDataMap jobDataMap, String definitionFilePath) 150 { 151 StringBuilder parameters = new StringBuilder(); 152 153 // definition file parameter 154 parameters.append("file=").append(definitionFilePath); 155 156 // variables parameters 157 String variablesAsString = (String) jobDataMap.get(Scheduler.PARAM_VALUES_PREFIX + VARIABLES_KEY); 158 Map<String, Object> variablesAsMap = _jsonUtils.convertJsonToMap(variablesAsString); 159 for (Map.Entry<String, Object> variable : variablesAsMap.entrySet()) 160 { 161 parameters.append("&").append(variable.getKey()).append("=").append(URLEncoder.encodeParameter(String.valueOf(variable.getValue()))); 162 } 163 164 // stylesheet 165 String xsltFilePath = (String) jobDataMap.get(Scheduler.PARAM_VALUES_PREFIX + XSLT_FILE_PATH_KEY); 166 if (StringUtils.isNotEmpty(xsltFilePath)) 167 { 168 parameters.append("&").append("xslt=").append(xsltFilePath); 169 } 170 171 return parameters.toString(); 172 } 173 174 private String _getResultFileName(String definitionFilePath, String format) 175 { 176 String[] definitionFilePathSegments = definitionFilePath.split(Pattern.quote(File.separator)); 177 String definitionFileName = definitionFilePathSegments[definitionFilePathSegments.length - 1]; 178 179 int lastIndexOfDot = definitionFileName.lastIndexOf('.'); 180 if (-1 != lastIndexOfDot) 181 { 182 definitionFileName = definitionFileName.substring(0, lastIndexOfDot); 183 } 184 185 String extractionDate = ZonedDateTime.now().format(RESULT_FILE_NAME_DATE_TIME_FORMATTER); 186 187 StringBuilder resultFileName = new StringBuilder(); 188 resultFileName.append(definitionFileName).append("-").append(extractionDate); 189 resultFileName.append(".").append(PDF_FORMAT_PARAM_VALUE.equals(format) ? PDF_FORMAT_SUFFIX : XML_FORMAT_SUFFIX); 190 return resultFileName.toString(); 191 } 192 193 private String _getSuccessMailBody(String definitionFileName) 194 { 195 I18nizableText bodyKey = new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_SUCCESS_BODY", Collections.singletonList(definitionFileName)); 196 return _i18nUtils.translate(bodyKey, null); // FIXME Use user preference language 197 } 198 199 private String _getFailureMailBody (String definitionFileName, String errorMessage) 200 { 201 StringBuilder body = new StringBuilder(); 202 203 I18nizableText bodyKey = new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_FAILURE_BODY", Collections.singletonList(definitionFileName)); 204 String intro = _i18nUtils.translate(bodyKey, null); // FIXME Use user preference language 205 206 body.append(intro).append("\n").append(errorMessage); 207 return body.toString(); 208 } 209 210 private void _sendMail(String recipient, String body) 211 { 212 String subject = _getMailSubject(); 213 try 214 { 215 SendMailHelper.sendMail(subject, null, body, recipient, _mailFrom); 216 } 217 catch (MessagingException e) 218 { 219 if (getLogger().isWarnEnabled()) 220 { 221 getLogger().warn("Fail to send email to " + recipient, e); 222 } 223 } 224 } 225 226 private String _getMailSubject () 227 { 228 I18nizableText subjectKey = new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_MAIL_SUBJECT"); 229 return _i18nUtils.translate(subjectKey, null); // FIXME Use user preference language 230 } 231}