001package org.apache.archiva.transaction; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.checksum.ChecksumAlgorithm; 023import org.apache.archiva.checksum.ChecksummedFile; 024import org.apache.commons.io.FileUtils; 025 026import java.io.IOException; 027import java.nio.charset.Charset; 028import java.nio.file.Files; 029import java.nio.file.Path; 030import java.nio.file.Paths; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.HashMap; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037import java.util.stream.Stream; 038 039/** 040 * Abstract class for the TransactionEvents 041 * 042 * 043 */ 044public abstract class AbstractTransactionEvent 045 implements TransactionEvent 046{ 047 private Map<Path, Path> backups = new HashMap<>(); 048 049 private List<Path> createdDirs = new ArrayList<>(); 050 051 private List<Path> createdFiles = new ArrayList<>(); 052 053 private List<ChecksumAlgorithm> checksumAlgorithms; 054 055 protected AbstractTransactionEvent() 056 { 057 this( new ArrayList<ChecksumAlgorithm>( 0 ) ); 058 } 059 060 protected AbstractTransactionEvent( List<ChecksumAlgorithm> checksumAlgorithms) 061 { 062 this.checksumAlgorithms = checksumAlgorithms; 063 } 064 065 protected List<ChecksumAlgorithm> getChecksumAlgorithms() 066 { 067 return checksumAlgorithms; 068 } 069 070 /** 071 * Method that creates a directory as well as all the parent directories needed 072 * 073 * @param dir The File directory to be created 074 * @throws IOException when an unrecoverable error occurred 075 */ 076 protected void mkDirs( Path dir ) 077 throws IOException 078 { 079 List<Path> createDirs = new ArrayList<>(); 080 081 Path parent = dir; 082 while ( !Files.exists(parent) || !Files.isDirectory(parent) ) 083 { 084 createDirs.add( parent ); 085 086 parent = parent.getParent(); 087 } 088 089 while ( !createDirs.isEmpty() ) 090 { 091 Path directory = createDirs.remove( createDirs.size() - 1 ); 092 Files.createDirectories(directory); 093 createdDirs.add( directory ); 094 } 095 } 096 097 protected void revertMkDirs() 098 throws IOException 099 { 100 if ( createdDirs != null ) 101 { 102 Collections.reverse( createdDirs ); 103 104 while ( !createdDirs.isEmpty() ) 105 { 106 Path dir = createdDirs.remove( 0 ); 107 108 if ( Files.isDirectory(dir)) 109 { 110 try(Stream<Path> str = Files.list(dir)) { 111 if (str.count()==0) { 112 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir); 113 } 114 } 115 } 116 else 117 { 118 //cannot rollback created directory if it still contains files 119 break; 120 } 121 } 122 } 123 } 124 125 protected void revertFilesCreated() 126 throws IOException 127 { 128 Iterator<Path> it = createdFiles.iterator(); 129 while ( it.hasNext() ) 130 { 131 Path file = it.next(); 132 Files.deleteIfExists(file); 133 it.remove(); 134 } 135 } 136 137 protected void createBackup( Path file ) 138 throws IOException 139 { 140 if ( Files.exists(file) && Files.isRegularFile(file) ) 141 { 142 Path backup = Files.createTempFile( "temp-", ".backup" ); 143 144 FileUtils.copyFile( file.toFile(), backup.toFile() ); 145 146 backup.toFile().deleteOnExit(); 147 148 backups.put( file, backup ); 149 } 150 } 151 152 protected void restoreBackups() 153 throws IOException 154 { 155 for ( Map.Entry<Path, Path> entry : backups.entrySet() ) 156 { 157 FileUtils.copyFile( entry.getValue().toFile(), entry.getKey().toFile() ); 158 } 159 } 160 161 protected void restoreBackup( Path file ) 162 throws IOException 163 { 164 Path backup = backups.get( file ); 165 if ( backup != null ) 166 { 167 FileUtils.copyFile( backup.toFile(), file.toFile() ); 168 } 169 } 170 171 /** 172 * Create checksums of file using all digesters defined at construction time. 173 * 174 * @param file 175 * @param force whether existing checksums should be overwritten or not 176 * @throws IOException 177 */ 178 protected void createChecksums( Path file, boolean force ) 179 throws IOException 180 { 181 for ( ChecksumAlgorithm checksumAlgorithm : getChecksumAlgorithms() ) 182 { 183 Path checksumFile = Paths.get( file.toAbsolutePath( ) + "." + getChecksumFileExtension( checksumAlgorithm ) ); 184 if ( Files.exists( checksumFile ) ) 185 { 186 if ( !force ) 187 { 188 continue; 189 } 190 createBackup( checksumFile ); 191 } 192 else 193 { 194 createdFiles.add( checksumFile ); 195 } 196 } 197 ChecksummedFile csFile = new ChecksummedFile( file ); 198 csFile.fixChecksums( getChecksumAlgorithms() ); 199 } 200 201 /** 202 * TODO: Remove in favor of using FileUtils directly. 203 */ 204 protected void writeStringToFile( Path file, String content ) 205 throws IOException 206 { 207 org.apache.archiva.common.utils.FileUtils.writeStringToFile( file, Charset.defaultCharset(), content ); 208 } 209 210 /** 211 * File extension for checksums 212 * TODO should be moved to plexus-digester ? 213 */ 214 protected String getChecksumFileExtension( ChecksumAlgorithm algorithm ) 215 { 216 return algorithm.getExt().get(0); 217 } 218 219}