001    package org.apache.archiva.consumers.core.repository;
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.archiva.metadata.repository.RepositorySession;
023    import org.apache.archiva.repository.events.RepositoryListener;
024    import org.apache.commons.lang.time.DateUtils;
025    import org.apache.archiva.common.utils.VersionComparator;
026    import org.apache.archiva.common.utils.VersionUtil;
027    import org.apache.archiva.model.ArtifactReference;
028    import org.apache.archiva.model.VersionedReference;
029    import org.apache.archiva.repository.ContentNotFoundException;
030    import org.apache.archiva.repository.ManagedRepositoryContent;
031    import org.apache.archiva.repository.layout.LayoutException;
032    
033    import java.io.File;
034    import java.text.ParseException;
035    import java.text.SimpleDateFormat;
036    import java.util.ArrayList;
037    import java.util.Calendar;
038    import java.util.Collections;
039    import java.util.Date;
040    import java.util.List;
041    import java.util.Set;
042    import java.util.regex.Matcher;
043    
044    /**
045     * Purge from repository all snapshots older than the specified days in the repository configuration.
046     */
047    public class DaysOldRepositoryPurge
048        extends AbstractRepositoryPurge
049    {
050        private SimpleDateFormat timestampParser;
051    
052        private int daysOlder;
053    
054        private int retentionCount;
055    
056        public DaysOldRepositoryPurge( ManagedRepositoryContent repository, int daysOlder, int retentionCount,
057                                       RepositorySession repositorySession, List<RepositoryListener> listeners )
058        {
059            super( repository, repositorySession, listeners );
060            this.daysOlder = daysOlder;
061            this.retentionCount = retentionCount;
062            timestampParser = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
063            timestampParser.setTimeZone( DateUtils.UTC_TIME_ZONE );
064        }
065    
066        public void process( String path )
067            throws RepositoryPurgeException
068        {
069            try
070            {
071                File artifactFile = new File( repository.getRepoRoot(), path );
072    
073                if ( !artifactFile.exists() )
074                {
075                    return;
076                }
077    
078                ArtifactReference artifact = repository.toArtifactReference( path );
079    
080                Calendar olderThanThisDate = Calendar.getInstance( DateUtils.UTC_TIME_ZONE );
081                olderThanThisDate.add( Calendar.DATE, -daysOlder );
082    
083                // respect retention count
084                VersionedReference reference = new VersionedReference();
085                reference.setGroupId( artifact.getGroupId() );
086                reference.setArtifactId( artifact.getArtifactId() );
087                reference.setVersion( artifact.getVersion() );
088    
089                List<String> versions = new ArrayList<String>( repository.getVersions( reference ) );
090    
091                Collections.sort( versions, VersionComparator.getInstance() );
092    
093                if ( retentionCount > versions.size() )
094                {
095                    // Done. nothing to do here. skip it.
096                    return;
097                }
098    
099                int countToPurge = versions.size() - retentionCount;
100    
101                for ( String version : versions )
102                {
103                    if ( countToPurge-- <= 0 )
104                    {
105                        break;
106                    }
107    
108                    ArtifactReference newArtifactReference = repository.toArtifactReference(
109                        artifactFile.getAbsolutePath() );
110                    newArtifactReference.setVersion( version );
111    
112                    File newArtifactFile = repository.toFile( newArtifactReference );
113    
114                    // Is this a generic snapshot "1.0-SNAPSHOT" ?
115                    if ( VersionUtil.isGenericSnapshot( newArtifactReference.getVersion() ) )
116                    {
117                        if ( newArtifactFile.lastModified() < olderThanThisDate.getTimeInMillis() )
118                        {
119                            doPurgeAllRelated( newArtifactReference );
120                        }
121                    }
122                    // Is this a timestamp snapshot "1.0-20070822.123456-42" ?
123                    else if ( VersionUtil.isUniqueSnapshot( newArtifactReference.getVersion() ) )
124                    {
125                        Calendar timestampCal = uniqueSnapshotToCalendar( newArtifactReference.getVersion() );
126    
127                        if ( timestampCal.getTimeInMillis() < olderThanThisDate.getTimeInMillis() )
128                        {
129                            doPurgeAllRelated( newArtifactReference );
130                        }
131                    }
132                }
133            }
134            catch ( ContentNotFoundException e )
135            {
136                throw new RepositoryPurgeException( e.getMessage(), e );
137            }
138            catch ( LayoutException e )
139            {
140                log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
141            }
142        }
143    
144        private Calendar uniqueSnapshotToCalendar( String version )
145        {
146            // The latestVersion will contain the full version string "1.0-alpha-5-20070821.213044-8"
147            // This needs to be broken down into ${base}-${timestamp}-${build_number}
148    
149            Matcher m = VersionUtil.UNIQUE_SNAPSHOT_PATTERN.matcher( version );
150            if ( m.matches() )
151            {
152                Matcher mtimestamp = VersionUtil.TIMESTAMP_PATTERN.matcher( m.group( 2 ) );
153                if ( mtimestamp.matches() )
154                {
155                    String tsDate = mtimestamp.group( 1 );
156                    String tsTime = mtimestamp.group( 2 );
157    
158                    Date versionDate;
159                    try
160                    {
161                        versionDate = timestampParser.parse( tsDate + "." + tsTime );
162                        Calendar cal = Calendar.getInstance( DateUtils.UTC_TIME_ZONE );
163                        cal.setTime( versionDate );
164    
165                        return cal;
166                    }
167                    catch ( ParseException e )
168                    {
169                        // Invalid Date/Time
170                        return null;
171                    }
172                }
173            }
174            return null;
175        }
176    
177        private void doPurgeAllRelated( ArtifactReference reference )
178        {
179            try
180            {
181                Set<ArtifactReference> related = repository.getRelatedArtifacts( reference );
182                purge( related );
183            }
184            catch ( ContentNotFoundException e )
185            {
186                // Nothing to do here - it means the repository would have been constructed incorrectly
187                log.debug( e.getMessage(), e );
188            }
189        }
190    }