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}