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