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