001/*
002 *  Copyright 2019 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.IOException;
019import java.nio.file.Path;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Optional;
023import java.util.stream.Stream;
024import java.util.zip.ZipOutputStream;
025
026import javax.jcr.Node;
027import javax.jcr.NodeIterator;
028import javax.jcr.RepositoryException;
029
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.slf4j.Logger;
034
035import org.ametys.plugins.repository.AmetysObjectResolver;
036import org.ametys.plugins.repository.collection.AmetysObjectCollection;
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.runtime.plugin.component.AbstractLogEnabled;
039
040/**
041 * Archives contents in /ametys:root/ametys:contents
042 */
043public class ContentArchiver extends AbstractLogEnabled implements Archiver, Serviceable
044{
045    /** Archiver id. */
046    public static final String ID = "contents";
047    
048    private AmetysObjectResolver _resolver;
049    private ContentsArchiverHelper _contentsArchiverHelper;
050    
051    private ManifestReaderWriter _manifestReaderWriter = new ContentArchiverManifestReaderWriter(getLogger());
052    
053    @Override
054    public void service(ServiceManager manager) throws ServiceException
055    {
056        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
057        _contentsArchiverHelper = (ContentsArchiverHelper) manager.lookup(ContentsArchiverHelper.ROLE);
058    }
059    
060    @Override
061    public ManifestReaderWriter getManifestReaderWriter()
062    {
063        return _manifestReaderWriter;
064    }
065    
066    @Override
067    public void export(ZipOutputStream zos) throws IOException
068    {
069        AmetysObjectCollection rootContents = _rootContents();
070        _contentsArchiverHelper.exportContents("contents/", rootContents, zos);
071    }
072    
073    private AmetysObjectCollection _rootContents()
074    {
075        return _resolver.resolveByPath("/ametys:contents");
076    }
077    
078    private void _deleteBeforePartialImport(AmetysObjectCollection rootContents) throws IOException
079    {
080        try
081        {
082            Node rootContentsNode = rootContents.getNode();
083            NodeIterator rootChildren = rootContentsNode.getNodes();
084            while (rootChildren.hasNext())
085            {
086                rootChildren.nextNode().remove();
087            }
088            rootContentsNode.getSession().save();
089        }
090        catch (RepositoryException e)
091        {
092            throw new IOException(e);
093        }
094    }
095    
096    @Override
097    public Collection<String> managedPartialImports(Collection<String> partialImports)
098    {
099        return partialImports.contains(ID)
100                ? Collections.singletonList(ID)
101                : Collections.EMPTY_LIST;
102    }
103    
104    @Override
105    public ImportReport partialImport(Path zipPath, Collection<String> partialImports, Merger merger, boolean deleteBefore) throws IOException
106    {
107        AmetysObjectCollection rootContents = _rootContents();
108        if (deleteBefore)
109        {
110            _deleteBeforePartialImport(rootContents);
111        }
112        return _contentsArchiverHelper.importContents("contents/", rootContents, zipPath, merger, Collections.EMPTY_SET);
113    }
114    
115    private static final class ContentArchiverManifestReaderWriter implements ManifestReaderWriter
116    {
117        private static final String __DATA = "contents";
118        
119        private Logger _logger;
120        
121        public ContentArchiverManifestReaderWriter(Logger logger)
122        {
123            _logger = logger;
124        }
125        
126        @Override
127        public Object getData()
128        {
129            return __DATA;
130        }
131        
132        @Override
133        public Stream<PartialImport> toPartialImports(Object data)
134        {
135            return Optional.ofNullable(data)
136                    .filter(String.class::isInstance)
137                    .map(String.class::cast)
138                    .filter(__DATA::equals)
139                    .map(Stream::of)
140                    .orElseGet(() ->
141                    {
142                        _logger.warn("Unexpected manifest data '{}', we would expect '{}', the ZIP archive probably comes from a different version of Ametys.", data, __DATA);
143                        return Stream.empty();
144                    })
145                    .map(contentsStr -> PartialImport.of(contentsStr, new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_CONTENT_ARCHIVER_OPTION_ALL")));
146        }
147    }
148}