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.nio.file.Path;
019import java.util.Optional;
020
021import org.slf4j.Logger;
022import org.w3c.dom.Document;
023
024import org.ametys.plugins.contentio.archive.Archivers.AmetysObjectNotImportedException;
025import org.ametys.plugins.contentio.archive.ImportReport.ImportError;
026import org.ametys.plugins.contentio.archive.Merger.AfterMerge;
027
028/**
029 * Unitary importer of an abstract object into Ametys.
030 * @param <O> The type of object to import
031 */
032public interface UnitaryImporter<O>
033{
034    /**
035     * The name for the object to import in error logs
036     * @return The name for the object to import in error logs
037     */
038    String objectNameForLogs();
039    
040    /**
041     * Gets the properties XML file from its ZIP path
042     * @param zipEntryPath The ZIP entry path of the XML file
043     * @return The properties XML file
044     * @throws Exception if an error occurs
045     */
046    Document getPropertiesXml(Path zipEntryPath) throws Exception;
047    
048    /**
049     * Retrieves the id of the object to import from the XML file
050     * @param propertiesXml The properties XML file
051     * @return The id of the object to import
052     * @throws Exception if an error occurs
053     */
054    String retrieveId(Document propertiesXml) throws Exception;
055    
056    /**
057     * Creates the object with the given id and XML properties file.
058     * <br>Note that the implementation must create the object with the given id, but it can also create sub-objects for this unitary object (for instance a Resource as an attachment for a Content, etc.).
059     * @param zipEntryPath The ZIP entry path of the XML file
060     * @param id The id of the object to import
061     * @param propertiesXml The properties XML file
062     * @return The created object
063     * @throws AmetysObjectNotImportedException If the object was not imported due to an exception (for instance an attribute is missing in the XML file). Then the object is ignored and the import will continue to the next object.
064     * @throws ImportGlobalFailException If the global import process must stop due to any fatal exception.
065     * @throws Exception if another unexpected error occurs
066     */
067    O create(Path zipEntryPath, String id, Document propertiesXml) throws AmetysObjectNotImportedException, ImportGlobalFailException, Exception;
068    
069    /**
070     * The {@link ImportReport}
071     * @return The {@link ImportReport}
072     */
073    ImportReport getReport();
074    
075    /**
076     * The optional process to do in a `finally` block in {@link #unitaryImport}
077     */
078    default void unitaryImportFinalize()
079    {
080        // Do nothing by default
081    }
082    
083    /**
084     * Does the unitary import.
085     * <br>Do not override the default behavior.
086     * @param zipArchivePath The ZIP file
087     * @param zipEntryPath The ZIP entry path of the XML file
088     * @param merger The {@link Merger}
089     * @param logger The logger
090     * @return The optionally created object
091     * @throws ImportGlobalFailException If the global import process must stop due to any fatal exception.
092     */
093    default Optional<O> unitaryImport(Path zipArchivePath, Path zipEntryPath, Merger merger, Logger logger) throws ImportGlobalFailException
094    {
095        try
096        {
097            Document propertiesXml = getPropertiesXml(zipEntryPath);
098            
099            String id = retrieveId(propertiesXml);
100            if (merger.needsMerge(id))
101            {
102                AfterMerge afterMerge = merger.merge(id);
103                switch (afterMerge)
104                {
105                    case STOP_PROCESS:
106                        // stop the object creation, process next object id
107                        return Optional.empty();
108                    case CONTINUE_PROCESS:
109                    default:
110                        // continue the object creation
111                        break;
112                }
113            }
114            
115            O created = create(zipEntryPath, id, propertiesXml);
116            return Optional.of(created);
117        }
118        catch (ImportGlobalFailException e)
119        {
120            getReport().addError(new ImportError(e));
121            throw e;
122        }
123        catch (AmetysObjectNotImportedException e)
124        {
125            logger.error("{} for '{}!{}' could not be imported.", objectNameForLogs(), zipArchivePath, zipEntryPath, e);
126            getReport().addError(new ImportError(e));
127            return Optional.empty();
128        }
129        catch (Exception e)
130        {
131            logger.error("An unexpected exception occured when trying to import {} for '{}!{}'.", objectNameForLogs(), zipArchivePath, zipEntryPath, e);
132            getReport().addError(new ImportError(e));
133            return Optional.empty();
134        }
135        finally
136        {
137            unitaryImportFinalize();
138        }
139    }
140}
141