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