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