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.commons.io.FileUtils; 023import org.codehaus.plexus.digest.Digester; 024import org.codehaus.plexus.digest.DigesterException; 025 026import java.io.File; 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Collections; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Map; 034 035/** 036 * Abstract class for the TransactionEvents 037 * 038 * 039 */ 040public abstract class AbstractTransactionEvent 041 implements TransactionEvent 042{ 043 private Map<File, File> backups = new HashMap<>(); 044 045 private List<File> createdDirs = new ArrayList<>(); 046 047 private List<File> createdFiles = new ArrayList<>(); 048 049 /** 050 * {@link List}<{@link Digester}> 051 */ 052 private List<? extends Digester> digesters; 053 054 protected AbstractTransactionEvent() 055 { 056 this( new ArrayList<Digester>( 0 ) ); 057 } 058 059 protected AbstractTransactionEvent( List<? extends Digester> digesters ) 060 { 061 this.digesters = digesters; 062 } 063 064 protected List<? extends Digester> getDigesters() 065 { 066 return digesters; 067 } 068 069 /** 070 * Method that creates a directory as well as all the parent directories needed 071 * 072 * @param dir The File directory to be created 073 * @throws IOException when an unrecoverable error occurred 074 */ 075 protected void mkDirs( File dir ) 076 throws IOException 077 { 078 List<File> createDirs = new ArrayList<>(); 079 080 File parent = dir; 081 while ( !parent.exists() || !parent.isDirectory() ) 082 { 083 createDirs.add( parent ); 084 085 parent = parent.getParentFile(); 086 } 087 088 while ( !createDirs.isEmpty() ) 089 { 090 File directory = createDirs.remove( createDirs.size() - 1 ); 091 092 if ( directory.mkdir() ) 093 { 094 createdDirs.add( directory ); 095 } 096 else 097 { 098 throw new IOException( "Failed to create directory: " + directory.getAbsolutePath() ); 099 } 100 } 101 } 102 103 protected void revertMkDirs() 104 throws IOException 105 { 106 if ( createdDirs != null ) 107 { 108 Collections.reverse( createdDirs ); 109 110 while ( !createdDirs.isEmpty() ) 111 { 112 File dir = (File) createdDirs.remove( 0 ); 113 114 if ( dir.isDirectory() && dir.list().length == 0 ) 115 { 116 FileUtils.deleteDirectory( dir ); 117 } 118 else 119 { 120 //cannot rollback created directory if it still contains files 121 break; 122 } 123 } 124 } 125 } 126 127 protected void revertFilesCreated() 128 throws IOException 129 { 130 Iterator<File> it = createdFiles.iterator(); 131 while ( it.hasNext() ) 132 { 133 File file = (File) it.next(); 134 file.delete(); 135 it.remove(); 136 } 137 } 138 139 protected void createBackup( File file ) 140 throws IOException 141 { 142 if ( file.exists() && file.isFile() ) 143 { 144 File backup = File.createTempFile( "temp-", ".backup" ); 145 146 FileUtils.copyFile( file, backup ); 147 148 backup.deleteOnExit(); 149 150 backups.put( file, backup ); 151 } 152 } 153 154 protected void restoreBackups() 155 throws IOException 156 { 157 for ( Map.Entry<File, File> entry : backups.entrySet() ) 158 { 159 FileUtils.copyFile( entry.getValue(), entry.getKey() ); 160 } 161 } 162 163 protected void restoreBackup( File file ) 164 throws IOException 165 { 166 File backup = (File) backups.get( file ); 167 if ( backup != null ) 168 { 169 FileUtils.copyFile( backup, file ); 170 } 171 } 172 173 /** 174 * Create checksums of file using all digesters defined at construction time. 175 * 176 * @param file 177 * @param force whether existing checksums should be overwritten or not 178 * @throws IOException 179 */ 180 protected void createChecksums( File file, boolean force ) 181 throws IOException 182 { 183 for ( Digester digester : getDigesters() ) 184 { 185 File checksumFile = new File( file.getAbsolutePath() + "." + getDigesterFileExtension( digester ) ); 186 if ( checksumFile.exists() ) 187 { 188 if ( !force ) 189 { 190 continue; 191 } 192 createBackup( checksumFile ); 193 } 194 else 195 { 196 createdFiles.add( checksumFile ); 197 } 198 199 try 200 { 201 writeStringToFile( checksumFile, digester.calc( file ) ); 202 } 203 catch ( DigesterException e ) 204 { 205 throw (IOException) e.getCause(); 206 } 207 } 208 } 209 210 /** 211 * TODO: Remove in favor of using FileUtils directly. 212 */ 213 protected void writeStringToFile( File file, String content ) 214 throws IOException 215 { 216 FileUtils.writeStringToFile( file, content ); 217 } 218 219 /** 220 * File extension for checksums 221 * TODO should be moved to plexus-digester ? 222 */ 223 protected String getDigesterFileExtension( Digester digester ) 224 { 225 return digester.getAlgorithm().toLowerCase().replaceAll( "-", "" ); 226 } 227 228}