001    package 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    
022    import org.apache.commons.io.FileUtils;
023    import org.codehaus.plexus.digest.Digester;
024    import org.codehaus.plexus.digest.DigesterException;
025    
026    import java.io.File;
027    import java.io.IOException;
028    import java.util.ArrayList;
029    import java.util.Collections;
030    import java.util.HashMap;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Map;
034    
035    /**
036     * Abstract class for the TransactionEvents
037     *
038     *
039     */
040    public abstract class AbstractTransactionEvent
041        implements TransactionEvent
042    {
043        private Map<File, File> backups = new HashMap<File, File>();
044    
045        private List<File> createdDirs = new ArrayList<File>();
046    
047        private List<File> createdFiles = new ArrayList<File>();
048    
049        /**
050         * {@link List}&lt;{@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<File>();
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 = (File) 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    }