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}