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.RepositoryException;
028import javax.jcr.Session;
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.explorer.resources.jcr.JCRResourcesCollection;
036import org.ametys.plugins.repository.AmetysObjectResolver;
037import org.ametys.plugins.repository.UnknownAmetysObjectException;
038import org.ametys.plugins.repository.jcr.JCRAmetysObject;
039import org.ametys.runtime.i18n.I18nizableText;
040import org.ametys.runtime.plugin.component.AbstractLogEnabled;
041
042/**
043 * Archives resources in /ametys:root/ametys:resources
044 */
045public class ResourcesArchiver extends AbstractLogEnabled implements Archiver, Serviceable
046{
047    /** Archiver id. */
048    public static final String ID = "resources";
049    
050    private AmetysObjectResolver _resolver;
051    private ResourcesArchiverHelper _resourcesArchiverHelper;
052    
053    private ManifestReaderWriter _manifestReaderWriter = new ResourcesArchiverManifestReaderWriter(getLogger());
054    
055    @Override
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
059        _resourcesArchiverHelper = (ResourcesArchiverHelper) manager.lookup(ResourcesArchiverHelper.ROLE);
060    }
061    
062    @Override
063    public ManifestReaderWriter getManifestReaderWriter()
064    {
065        return _manifestReaderWriter;
066    }
067    
068    @Override
069    public void export(ZipOutputStream zos) throws IOException
070    {
071        Optional<JCRResourcesCollection> rootResources = _rootResources();
072        if (rootResources.isPresent())
073        {
074            _resourcesArchiverHelper.exportCollection(rootResources.get(), zos, "resources/");
075        }
076    }
077    
078    private Optional<JCRResourcesCollection> _rootResources()
079    {
080        try
081        {
082            return Optional.of(_resolver.resolveByPath("/ametys:resources"));
083        }
084        catch (UnknownAmetysObjectException e)
085        {
086            return Optional.empty();
087        }
088    }
089    
090    private void _deleteBeforePartialImport(JCRResourcesCollection rootResources) throws IOException
091    {
092        try
093        {
094            Node allResourcesNode = rootResources.getNode();
095            Session session = allResourcesNode.getParent().getSession();
096            allResourcesNode.remove();
097            session.save();
098        }
099        catch (RepositoryException e)
100        {
101            throw new IOException(e);
102        }
103    }
104    
105    @Override
106    public Collection<String> managedPartialImports(Collection<String> partialImports)
107    {
108        return partialImports.contains(ID)
109                ? Collections.singletonList(ID)
110                : Collections.EMPTY_LIST;
111    }
112    
113    @Override
114    public ImportReport partialImport(Path zipPath, Collection<String> partialImports, Merger merger, boolean deleteBefore) throws IOException
115    {
116        Optional<JCRResourcesCollection> rootResources = _rootResources();
117        if (rootResources.isPresent())
118        {
119            // ALWAYS remove root resources, even if deleteBefore is false because it MUST be re-created with the good UUID
120            _deleteBeforePartialImport(rootResources.get());
121        }
122        
123        JCRAmetysObject root = _resolver.resolveByPath("/");
124        Node parentOfRootResources = root.getNode();
125        return _resourcesArchiverHelper.importCollection("resources/", parentOfRootResources, zipPath, merger);
126    }
127    
128    private static final class ResourcesArchiverManifestReaderWriter implements ManifestReaderWriter
129    {
130        private static final String __DATA = "resources";
131        
132        private Logger _logger;
133        
134        public ResourcesArchiverManifestReaderWriter(Logger logger)
135        {
136            _logger = logger;
137        }
138        
139        @Override
140        public Object getData()
141        {
142            return __DATA;
143        }
144        
145        @Override
146        public Stream<PartialImport> toPartialImports(Object data)
147        {
148            return Optional.ofNullable(data)
149                    .filter(String.class::isInstance)
150                    .map(String.class::cast)
151                    .filter(__DATA::equals)
152                    .map(Stream::of)
153                    .orElseGet(() ->
154                    {
155                        _logger.warn("Unexpected manifest data '{}', we would expect '{}', the ZIP archive probably comes from a different version of Ametys.", data, __DATA);
156                        return Stream.empty();
157                    })
158                    .map(resourcesStr -> PartialImport.of(resourcesStr, new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_ARCHIVE_IMPORT_RESOURCE_ARCHIVER_OPTION_ALL")));
159        }
160    }
161}