1212 */
1313package org .sonatype .nexus .proxy .item ;
1414
15+ import java .io .File ;
1516import java .io .InputStream ;
1617import java .security .DigestInputStream ;
1718import java .security .MessageDigest ;
2627import org .sonatype .nexus .proxy .attributes .inspectors .DigestCalculatingInspector ;
2728import org .sonatype .nexus .proxy .maven .MUtils ;
2829import org .sonatype .nexus .proxy .repository .Repository ;
30+ import org .sonatype .nexus .proxy .storage .local .fs .DefaultFSLocalRepositoryStorage ;
2931import org .sonatype .nexus .proxy .walker .AbstractFileWalkerProcessor ;
3032import org .sonatype .nexus .proxy .walker .DefaultWalkerContext ;
3133import org .sonatype .nexus .proxy .walker .Walker ;
3537import org .sonatype .sisu .goodies .common .ComponentSupport ;
3638
3739import com .google .common .io .ByteStreams ;
40+ import org .apache .commons .lang .StringUtils ;
41+
42+ import static org .sonatype .nexus .proxy .attributes .inspectors .DigestCalculatingInspector .DIGEST_MD5_KEY ;
43+ import static org .sonatype .nexus .proxy .attributes .inspectors .DigestCalculatingInspector .DIGEST_SHA1_KEY ;
44+ import static org .sonatype .nexus .proxy .maven .ChecksumContentValidator .ATTR_REMOTE_MD5 ;
45+ import static org .sonatype .nexus .proxy .maven .ChecksumContentValidator .ATTR_REMOTE_SHA1 ;
46+ import static org .sonatype .nexus .proxy .maven .ChecksumContentValidator .SUFFIX_MD5 ;
47+ import static org .sonatype .nexus .proxy .maven .ChecksumContentValidator .SUFFIX_SHA1 ;
3848
3949/**
4050 * Reconciles any item attribute checksums affected by NEXUS-8178.
@@ -53,6 +63,10 @@ public class ChecksumReconciler
5363
5464 private final MessageDigest md5 ;
5565
66+ private File attributesBaseDir ;
67+
68+ private long modifiedSinceMillis ;
69+
5670 @ Inject
5771 public ChecksumReconciler (final Walker walker , final DigestCalculatingInspector digestCalculatingInspector )
5872 throws NoSuchAlgorithmException
@@ -68,10 +82,18 @@ public ChecksumReconciler(final Walker walker, final DigestCalculatingInspector
6882 /**
6983 * Walks the selected sub-tree in the given repository attempting to reconcile their item attribute checksums.
7084 */
71- public void reconcileChecksums (final Repository repo , final ResourceStoreRequest request ) {
85+ public void reconcileChecksums (final Repository repo , final ResourceStoreRequest request , final long sinceMillis ) {
7286 final WalkerContext context = new DefaultWalkerContext (repo , request );
7387
7488 final String repositoryId = repo .getId ();
89+
90+ attributesBaseDir = getAttributesBaseDir (repo );
91+ modifiedSinceMillis = sinceMillis ;
92+
93+ if (attributesBaseDir == null ) {
94+ return ; // no point walking repository with no local storage
95+ }
96+
7597 context .getProcessors ().add (new AbstractFileWalkerProcessor ()
7698 {
7799 @ Override
@@ -97,17 +119,21 @@ protected void processFileItem(final WalkerContext ctx, final StorageFileItem it
97119 * Checks the checksum cached in the item's attributes to see if it needs reconciling with the stored content.
98120 */
99121 void reconcileItemChecksum (final Repository repo , final StorageFileItem item ) throws Exception {
100- final String itemPath = item .getPath ();
101- if (!itemPath .endsWith (".sha1" ) && !itemPath .endsWith (".md5" )) {
102- final String attributeSHA1 = item .getRepositoryItemAttributes ().get (DigestCalculatingInspector .DIGEST_SHA1_KEY );
103- if (attributeSHA1 != null ) {
122+
123+ final String localSHA1 = item .getRepositoryItemAttributes ().get (DIGEST_SHA1_KEY );
124+ final String remoteSHA1 = item .getRepositoryItemAttributes ().get (ATTR_REMOTE_SHA1 );
125+
126+ if (localSHA1 != null || remoteSHA1 != null ) {
127+
128+ final String itemPath = item .getPath ();
129+ if (shouldAttemptReconciliation (itemPath )) {
104130
105131 sha1 .reset ();
106132 md5 .reset ();
107133
108134 // look for checksums affected by the link persister pre-fetching the first 8 bytes (see NEXUS-8178)
109- try (final InputStream is = new DigestInputStream (new DigestInputStream ( //
110- item . getContentLocator (). getContent (), sha1 ), md5 )) {
135+ try (final InputStream is = new DigestInputStream (new DigestInputStream (item . getContentLocator (). getContent (),
136+ sha1 ), md5 )) {
111137
112138 final byte [] buf = new byte [8 ];
113139 ByteStreams .read (is , buf , 0 , 8 );
@@ -119,11 +145,16 @@ void reconcileItemChecksum(final Repository repo, final StorageFileItem item) th
119145
120146 final String affectedSHA1 = DigesterUtils .getDigestAsString (sha1 .digest ());
121147
122- if (attributeSHA1 .equals (affectedSHA1 )) {
148+ if ((localSHA1 != null && localSHA1 .equals (affectedSHA1 ))
149+ || (remoteSHA1 != null && remoteSHA1 .equals (affectedSHA1 ))) {
150+
123151 log .info ("Reconciling attribute checksums for {}" , item );
152+
124153 final RepositoryItemUid uid = item .getRepositoryItemUid ();
125154 uid .getLock ().lock (Action .update );
126155 try {
156+ item .getRepositoryItemAttributes ().remove (ATTR_REMOTE_SHA1 );
157+ item .getRepositoryItemAttributes ().remove (ATTR_REMOTE_MD5 );
127158 digestCalculatingInspector .processStorageItem (item );
128159 repo .getAttributesHandler ().storeAttributes (item );
129160 }
@@ -132,15 +163,42 @@ void reconcileItemChecksum(final Repository repo, final StorageFileItem item) th
132163 }
133164 }
134165
135- reconcileChecksumFile (repo , itemPath + ".sha1" , affectedSHA1 ,
136- item .getRepositoryItemAttributes ().get (DigestCalculatingInspector . DIGEST_SHA1_KEY ));
166+ reconcileChecksumFile (repo , itemPath + SUFFIX_SHA1 , affectedSHA1 ,
167+ item .getRepositoryItemAttributes ().get (DIGEST_SHA1_KEY ));
137168
138169 final String affectedMD5 = DigesterUtils .getDigestAsString (md5 .digest ());
139170
140- reconcileChecksumFile (repo , itemPath + ".md5" , affectedMD5 ,
141- item .getRepositoryItemAttributes ().get (DigestCalculatingInspector .DIGEST_MD5_KEY ));
171+ reconcileChecksumFile (repo , itemPath + SUFFIX_MD5 , affectedMD5 ,
172+ item .getRepositoryItemAttributes ().get (DIGEST_MD5_KEY ));
173+ }
174+ }
175+ }
176+
177+ /**
178+ * Determines the base directory of local attribute storage.
179+ */
180+ private File getAttributesBaseDir (final Repository repo ) {
181+ try {
182+ if (repo .getLocalStorage () instanceof DefaultFSLocalRepositoryStorage ) {
183+ final File baseDir = ((DefaultFSLocalRepositoryStorage ) repo .getLocalStorage ()).getBaseDir (repo , null );
184+ return new File (new File (baseDir , ".nexus" ), "attributes" );
142185 }
143186 }
187+ catch (final Exception e ) {
188+ log .warn ("Problem finding local storage for {}" , repo , e );
189+ }
190+ return null ;
191+ }
192+
193+ /**
194+ * Should we attempt checksum reconciliation for the given path?
195+ */
196+ private boolean shouldAttemptReconciliation (final String itemPath ) {
197+ if (itemPath == null || itemPath .endsWith (SUFFIX_SHA1 ) || itemPath .endsWith (SUFFIX_MD5 )) {
198+ return false ; // ignore associated checksum files, we'll fix them when we process the main item
199+ }
200+ final File attributesFile = new File (attributesBaseDir , StringUtils .strip (itemPath , "/" ));
201+ return attributesFile .lastModified () >= modifiedSinceMillis ;
144202 }
145203
146204 /**
0 commit comments