001/* 002 * Copyright 2020 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.contentio.archive; 017 018import java.io.File; 019import java.io.IOException; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Optional; 024import java.util.function.Predicate; 025import java.util.stream.Collectors; 026 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.commons.lang3.StringUtils; 030import org.quartz.JobDataMap; 031import org.quartz.JobExecutionContext; 032import org.quartz.JobExecutionException; 033 034import org.ametys.core.schedule.progression.ContainerProgressionTracker; 035import org.ametys.core.user.User; 036import org.ametys.plugins.contentio.archive.ImportReport.ImportError; 037import org.ametys.plugins.core.schedule.Scheduler; 038import org.ametys.runtime.i18n.I18nizableText; 039import org.ametys.runtime.i18n.I18nizableTextParameter; 040 041/** 042 * Job for importing data from an archive. 043 */ 044public class ImportArchiveSchedulable extends AbstractArchiveSchedulable 045{ 046 /** This schedulable id */ 047 public static final String ID = ImportArchiveSchedulable.class.getName(); 048 049 static final String ARCHIVE_KEY = "archive"; 050 static final String ELEMENTS_KEY = "elements"; 051 static final String MERGE_POLICY_KEY = "mergePolicy"; 052 053 private static final String __JOBDATAMAP_ARCHIVE_KEY = Scheduler.PARAM_VALUES_PREFIX + ARCHIVE_KEY; 054 private static final String __JOBDATAMAP_ELEMENTS_KEY = Scheduler.PARAM_VALUES_PREFIX + ELEMENTS_KEY; 055 private static final String __JOBDATAMAP_MERGE_POLICY_KEY = Scheduler.PARAM_VALUES_PREFIX + MERGE_POLICY_KEY; 056 057 private static final String __ERROR_TEMPLATE = "<li><pre>%s \n %s</pre></li>"; 058 059 private ArchiverExtensionPoint _archiverEP; 060 061 @Override 062 public void service(ServiceManager manager) throws ServiceException 063 { 064 super.service(manager); 065 _archiverEP = (ArchiverExtensionPoint) manager.lookup(ArchiverExtensionPoint.ROLE); 066 } 067 068 @Override 069 public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception 070 { 071 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); 072 String archiveName = jobDataMap.getString(__JOBDATAMAP_ARCHIVE_KEY); 073 Optional<Collection<String>> elements = Optional.of(__JOBDATAMAP_ELEMENTS_KEY) 074 .map(jobDataMap::get) 075 .filter(Collection.class::isInstance) 076 .map(Collection.class::cast); 077 MergePolicy mergePolicy = (MergePolicy) jobDataMap.get(__JOBDATAMAP_MERGE_POLICY_KEY); 078 Merger merger = mergePolicy.getMerger(); 079 080 User user = _getUser(); 081 String userEmail = null; 082 String language = _userLanguagesManager.getDefaultLanguage(); 083 if (user != null) 084 { 085 userEmail = user.getEmail(); 086 087 String userLanguage = user.getLanguage(); 088 language = StringUtils.defaultIfBlank(userLanguage, language); 089 } 090 091 File input = _getFileInput(archiveName); 092 093 getLogger().info("Importing archive {} ...", input.getAbsolutePath()); 094 long t0 = System.currentTimeMillis(); 095 096 boolean success = true; 097 try 098 { 099 ImportReport importReport = _import(input, elements, merger); 100 101 getLogger().info("Archive {} imported without error in {} ms", input.getAbsolutePath(), System.currentTimeMillis() - t0); 102 103 // Once finished with import, inform user 104 String subject = _i18nUtils.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_SUBJECT"), language); 105 String body = getSuccessMailBody(input, elements, importReport, language); 106 107 _sendMail(subject, body, userEmail); 108 109 success = importReport.getErrors().isEmpty(); 110 } 111 catch (Exception e) 112 { 113 // Importing encountered an error during execution, send error mail 114 String subject = _i18nUtils.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAILERROR_SUBJECT"), language); 115 String body = _i18nUtils.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAILERROR_BODY"), language); 116 117 _sendMail(subject, body, userEmail); 118 119 // rethrow to ensure the job is marked as failed 120 throw e; 121 } 122 123 // throw to ensure the job is marked as failed 124 if (!success) 125 { 126 throw new JobExecutionException("Errors occured during the import. See previous logs for more info."); 127 } 128 } 129 130 private ImportReport _import(File input, Optional<Collection<String>> elements, Merger merger) throws IOException 131 { 132 if (elements.isPresent()) 133 { 134 return _archiveHandler.partialImport(input, elements.get(), merger); 135 } 136 else 137 { 138 return _archiveHandler.importAll(input, merger); 139 } 140 } 141 142 private File _getFileInput(String archiveName) 143 { 144 return _archiveHandler.getArchiveFile(archiveName); 145 } 146 147 /** 148 * Gets the body of the success mail 149 * @param input The file input 150 * @param elements the imported elements or empty if all the archive was imported 151 * @param importReport The {@link ImportReport} 152 * @param language The language to use in the mail 153 * @return the body of the success mail 154 * @throws IOException If an I/O error occurs with the ZIP filesystem 155 */ 156 protected String getSuccessMailBody(File input, Optional<Collection<String>> elements, ImportReport importReport, String language) throws IOException 157 { 158 final String catalogue = "plugin.contentio"; 159 160 Map<String, I18nizableTextParameter> i18nParameters = new HashMap<>(); 161 162 String inputPath = input.getCanonicalPath(); 163 i18nParameters.put("archivePath", new I18nizableText(inputPath)); 164 i18nParameters.put("archiveName", new I18nizableText(input.getName())); 165 166 Collection<ImportError> errors = importReport.getErrors(); 167 168 StringBuilder body = new StringBuilder(); 169 170 if (errors.isEmpty()) 171 { 172 body.append(_i18nUtils.translate(new I18nizableText(catalogue, "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_SUCCESS_BODY_INTRO", i18nParameters), language)); 173 } 174 else if (errors.size() == 1) 175 { 176 body.append(_i18nUtils.translate(new I18nizableText(catalogue, "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_SUCCESS_BODY_INTRO_WITH_ERROR", i18nParameters), language)); 177 } 178 else 179 { 180 i18nParameters.put("nbErrors", new I18nizableText(String.valueOf(errors.size()))); 181 body.append(_i18nUtils.translate(new I18nizableText(catalogue, "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_SUCCESS_BODY_INTRO_WITH_ERRORS", i18nParameters), language)); 182 } 183 184 body.append("\n<br/><br/>") 185 .append(_i18nUtils.translate(new I18nizableText(catalogue, "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_SUCCESS_BODY_END"), language)); 186 187 Predicate<Archiver> filterArchiver = elements.isPresent() 188 ? archiver -> !archiver.managedPartialImports(elements.get()).isEmpty() 189 : archiver -> true; 190 191 // Join additional instruction 192 String additionalBody = _archiverEP.getExtensionsIds() 193 .stream() 194 .map(_archiverEP::getExtension) 195 .filter(filterArchiver) 196 .map(Archiver::additionalSuccessImportMail) 197 .flatMap(Collection::stream) 198 .distinct() 199 .map(adB -> _i18nUtils.translate(adB, language)) 200 .collect(Collectors.joining("<br/>")); 201 202 if (!additionalBody.isBlank()) 203 { 204 body.append("<br/>").append(additionalBody); 205 } 206 207 if (!errors.isEmpty()) 208 { 209 String errorItems = errors 210 .stream() 211 .map(importError -> String.format(__ERROR_TEMPLATE, importError.getMessage(), importError.getStackTrace())) 212 .collect(Collectors.joining("\n")); 213 214 body.append("\n<br/><br/>") 215 .append("\n" + _i18nUtils.translate(new I18nizableText(catalogue, "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_SCHEDULABLE_MAIL_BODY_LIST_OF_ERRORS"), language)) 216 .append("\n<br/>") 217 .append("\n<ul>\n") 218 .append(errorItems) 219 .append("\n</ul>"); 220 } 221 222 return body.toString(); 223 } 224}