Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote-tracking branch 'origin/master' into squash-o-matic-output

  • Loading branch information...
commit c69aaec05174f1dbf079b6f52e68ac75801d90d7 2 parents 8251e22 + 6531b65
@cstamas cstamas authored
Showing with 804 additions and 341 deletions.
  1. +1 −1  README.md
  2. +53 −46 nexus/nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/MUtils.java
  3. +1 −1  nexus/nexus-core/src/main/java/org/sonatype/nexus/proxy/repository/AbstractProxyRepository.java
  4. +1 −1  nexus/nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/remote/DefaultRemoteProviderHintFactory.java
  5. +3 −2 nexus/nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/remote/httpclient/HttpClientRemoteStorage.java
  6. +1 −0  nexus/nexus-core/src/test/java/org/sonatype/nexus/proxy/RemoteErrorPageWith200Test.java
  7. +14 −0 nexus/nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/MUtilsTest.java
  8. +7 −6 nexus/nexus-core/src/test/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSPeerTest.java
  9. +0 −2  nexus/nexus-oss-webapp/pom.xml
  10. +0 −80 nexus/nexus-oss-webapp/src/main/resources/content/conf/examples/jetty-redirect-access-log.xml
  11. +126 −0 .../nexus-proxy/src/main/java/org/sonatype/nexus/proxy/storage/remote/httpclient/InterruptableInputStream.java
  12. +1 −0  nexus/nexus-test/nexus-test-harness-its/resources/nexus570/projects/simple-archetype/pom.xml
  13. +38 −0 ...s-test-harness-its/src/test/java/org/sonatype/nexus/integrationtests/nexus570/Nexus570IndexArchetypeIT.java
  14. +9 −2 ...s-test-harness-launcher/src/main/java/org/sonatype/nexus/integrationtests/AbstractNexusIntegrationTest.java
  15. +14 −7 ...s/nexus-test/nexus-testsuite-support/src/main/java/org/sonatype/nexus/testsuite/support/NexusITSupport.java
  16. +1 −8 ...-test/nexus-testsuite-support/src/main/java/org/sonatype/nexus/testsuite/support/NexusRunningITSupport.java
  17. +24 −17 nexus/nexus-webapp/src/main/webapp/js/repoServer/RoleEditPanel.js
  18. +16 −13 ...dexer/nexus-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/events/IndexerManagerEventInspector.java
  19. +82 −27 ...ugins/indexer/nexus-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/index/DefaultIndexerManager.java
  20. +94 −0 ...gins/indexer/nexus-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/index/ForceableReentrantLock.java
  21. +10 −2 nexus/plugins/indexer/nexus-indexer-lucene-plugin/src/main/js/repoServer.SearchResultGrid.js
  22. +24 −2 ...ins/indexer/nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/DefaultIndexerManagerIT.java
  23. +150 −0 ...s-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/DeleteRepositoryWhileDownloadingIndexIT.java
  24. +128 −0 ...s/plugins/indexer/nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/ForceableLockTest.java
  25. +0 −114 nexus/plugins/indexer/nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/IndexingLockTest.java
  26. +0 −2  ...s/plugins/indexer/nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/RepositoryStateIT.java
  27. +6 −8 pom.xml
View
2  README.md
@@ -25,7 +25,7 @@ Example Maven settings XML:
<!-- This sends everything to Forge -->
<id>sonatype-forge</id>
<mirrorOf>external:*</mirrorOf>
- <url>https://repository.sonatype.org/content/groups/forge</url>
+ <url>https://repository.sonatype.org/content/groups/sonatype-public-grid/</url>
</mirror>
</mirrors>
<profiles>
View
99 nexus/nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/MUtils.java
@@ -16,6 +16,7 @@
import java.io.InputStream;
import java.util.Locale;
+import com.google.common.annotations.VisibleForTesting;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.sonatype.nexus.proxy.item.StorageFileItem;
@@ -47,61 +48,67 @@ public static String readDigestFromStream( final InputStream inputStream )
{
try
{
- String raw = StringUtils.chomp( IOUtil.toString( inputStream, "UTF-8" ) ).trim();
-
- if ( StringUtils.isEmpty( raw ) )
- {
- return "";
- }
+ return readDigest( IOUtil.toString( inputStream, "UTF-8" ) );
+ }
+ finally
+ {
+ IOUtil.close( inputStream );
+ }
+ }
- String digest;
- // digest string at end with separator, e.g.:
- // MD5 (pom.xml) = 68da13206e9dcce2db9ec45a9f7acd52
- // ant-1.5.jar: DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167
- if ( raw.contains( "=" ) || raw.contains( ":" ) )
- {
- digest = raw.split( "[=:]", 2 )[1].trim();
- }
- else
- {
- // digest string at start, e.g. '68da13206e9dcce2db9ec45a9f7acd52 pom.xml'
- digest = raw.split( " ", 2 )[0];
- }
+ @VisibleForTesting
+ static String readDigest( final String input )
+ {
+ String raw = StringUtils.chomp( input ).trim();
- if ( !isDigest( digest ) )
- {
- // maybe it's "uncompressed", e.g. 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167'
- digest = compress( digest );
- }
+ if ( StringUtils.isEmpty( raw ) )
+ {
+ return "";
+ }
- if ( !isDigest( digest ) )
- {
- // check if the raw string is an uncompressed checksum, e.g.
- // 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167'
- digest = compress( raw );
- }
+ String digest;
+ // digest string at end with separator, e.g.:
+ // MD5 (pom.xml) = 68da13206e9dcce2db9ec45a9f7acd52
+ // ant-1.5.jar: DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167
+ if ( raw.contains( "=" ) || raw.contains( ":" ) )
+ {
+ digest = raw.split( "[=:]", 2 )[1].trim();
+ }
+ else
+ {
+ // digest string at start, e.g. '68da13206e9dcce2db9ec45a9f7acd52 pom.xml'
+ digest = raw.split( " ", 2 )[0];
+ }
- if ( !isDigest( digest ) )
- {
- // check if the raw string is an uncompressed checksum with file name suffix, e.g.
- // 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167 pom.xml'
- digest = compress( raw.substring( 0, raw.lastIndexOf( " " ) ).trim() );
- }
+ if ( !isDigest( digest ) )
+ {
+ // maybe it's "uncompressed", e.g. 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167'
+ digest = compress( digest );
+ }
- if ( !isDigest( digest ) )
- {
- // we have to return some string even if it's not a valid digest, because 'null' is treated as
- // "checksum does not exist" elsewhere (AbstractChecksumContentValidator)
- // -> fallback to original behavior
- digest = raw.split( " ", 2 )[0];
- }
+ if ( !isDigest( digest ) )
+ {
+ // check if the raw string is an uncompressed checksum, e.g.
+ // 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167'
+ digest = compress( raw );
+ }
- return digest;
+ if ( !isDigest( digest ) && raw.contains( " " ) )
+ {
+ // check if the raw string is an uncompressed checksum with file name suffix, e.g.
+ // 'DCAB 88FC 2A04 3C24 79A6 DE67 6A2F 8179 E9EA 2167 pom.xml'
+ digest = compress( raw.substring( 0, raw.lastIndexOf( " " ) ).trim() );
}
- finally
+
+ if ( !isDigest( digest ) )
{
- IOUtil.close( inputStream );
+ // we have to return some string even if it's not a valid digest, because 'null' is treated as
+ // "checksum does not exist" elsewhere (AbstractChecksumContentValidator)
+ // -> fallback to original behavior
+ digest = raw.split( " ", 2 )[0];
}
+
+ return digest;
}
private static String compress( String digest )
View
2  ...nexus-core/src/main/java/org/sonatype/nexus/proxy/repository/AbstractProxyRepository.java
@@ -543,7 +543,7 @@ protected void setProxyMode( ProxyMode proxyMode, boolean sendNotification, Thro
// this one should be fired _always_
eventBus().post( new RepositoryEventProxyModeSet( this, oldProxyMode, proxyMode, cause ) );
- if ( !proxyMode.equals( oldProxyMode ) )
+ if ( proxyMode != null && !proxyMode.equals( oldProxyMode ) )
{
// this one should be fired on _transition_ only
eventBus().post( new RepositoryEventProxyModeChanged( this, oldProxyMode, proxyMode, cause ) );
View
2  ...c/main/java/org/sonatype/nexus/proxy/storage/remote/DefaultRemoteProviderHintFactory.java
@@ -92,7 +92,7 @@ public String getRoleHint( final String remoteUrl, final String hint )
throw new IllegalArgumentException( "RemoteRepositoryStorage hint cannot be null!" );
}
- logger.info( "Returning supplied \"{}\" hint for remote URL {}.", new Object[] { remoteUrl, hint } );
+ logger.debug( "Returning supplied \"{}\" hint for remote URL {}.", remoteUrl, hint );
return hint;
}
View
5 ...main/java/org/sonatype/nexus/proxy/storage/remote/httpclient/HttpClientRemoteStorage.java
@@ -175,7 +175,7 @@ public AbstractStorageItem retrieveItem( final ProxyRepository repository, final
InputStream is;
try
{
- is = httpResponse.getEntity().getContent();
+ is = new InterruptableInputStream( method, httpResponse.getEntity().getContent() );
String mimeType = ContentType.getOrDefault( httpResponse.getEntity() ).getMimeType();
if ( mimeType == null )
@@ -261,7 +261,8 @@ public void storeItem( final ProxyRepository repository, final StorageItem item
final InputStreamEntity entity;
try
{
- entity = new InputStreamEntity( fileItem.getInputStream(), fileItem.getLength() );
+ entity =
+ new InputStreamEntity( new InterruptableInputStream( fileItem.getInputStream() ), fileItem.getLength() );
}
catch ( IOException e )
{
View
1  nexus/nexus-core/src/test/java/org/sonatype/nexus/proxy/RemoteErrorPageWith200Test.java
@@ -65,6 +65,7 @@ public void testRemoteReturnsErrorWith200StatusHeadersNotSet()
String expectedContent = "my cool expected content";
ErrorServlet.CONTENT = expectedContent;
+ ErrorServlet.clearHeaders();
// remote request
ResourceStoreRequest storeRequest = new ResourceStoreRequest( "random/file.txt" );
View
14 nexus/nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/MUtilsTest.java
@@ -16,6 +16,7 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static org.sonatype.nexus.proxy.maven.MUtils.readDigest;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -83,4 +84,17 @@ public void zeroLengthSha1()
assertThat( "Zero bites checksum file", digest, is( equalTo( "" ) ) );
}
+ /**
+ * Invalid digest, so MUtil should return it as is.
+ */
+ @Test
+ public void invalidShortDigest()
+ {
+ assertThat( readDigest( "123456" ), is( "123456" ) );
+ assertThat( readDigest( "" ), is( "" ) );
+
+ // special case, because space is a delimiter that will be munched
+ assertThat( readDigest( " " ), is( "" ) );
+ }
+
}
View
13 ...nexus-core/src/test/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSPeerTest.java
@@ -54,10 +54,11 @@ public void hiddenTargetHandling()
File hiddenTarget = subject.getHiddenTarget( null, repoBase, target, file );
assertThat( hiddenTarget, notNullValue() );
assertThat( hiddenTarget, isFile() );
- // startsWith, as garbage is appeneded to it's end
+ // startsWith, as garbage is appended to it's end
assertThat( hiddenTarget.getName(), startsWith( "foo-1.0.txt" ) );
// contains, as OS path from root is prefixing this, and garbage at the end suffixing it
- assertThat( hiddenTarget.getPath(), containsString( "target/repoId/.nexus/tmp/foo-1.0.txt" ) );
+
+ assertThat( hiddenTarget.getPath(), containsString( "target/repoId/.nexus/tmp/foo-1.0.txt".replace("/", File.separator) ) );
// writing to hidden target is handled elsewhere, so we simulate content being written out
final String PAYLOAD = "dummy payload";
@@ -73,7 +74,7 @@ public void hiddenTargetHandling()
// name should be not garbaged anymore
assertThat( target.getName(), equalTo( "foo-1.0.txt" ) );
// path prefixed by OS from root, no garbage at tail
- assertThat( target.getPath(), endsWith( "target/repoId/foo/1.0/foo-1.0.txt" ) );
+ assertThat( target.getPath(), endsWith( "target/repoId/foo/1.0/foo-1.0.txt".replace("/", File.separator) ) );
// content is fine too
assertThat( FileUtils.fileRead( target ), equalTo( PAYLOAD ) );
}
@@ -100,10 +101,10 @@ public void hiddenTargetHandlingAtRoot()
File hiddenTarget = subject.getHiddenTarget( null, repoBase, target, file );
assertThat( hiddenTarget, notNullValue() );
assertThat( hiddenTarget, isFile() );
- // startsWith, as garbage is appeneded to it's end
+ // startsWith, as garbage is appended to it's end
assertThat( hiddenTarget.getName(), startsWith( "archetype-catalog.xml" ) );
// contains, as OS path from root is prefixing this, and garbage at the end suffixing it
- assertThat( hiddenTarget.getPath(), containsString( "target/repoId/.nexus/tmp/archetype-catalog.xml" ) );
+ assertThat( hiddenTarget.getPath(), containsString( "target/repoId/.nexus/tmp/archetype-catalog.xml".replace("/", File.separator) ) );
// writing to hidden target is handled elsewhere, so we simulate content being written out
final String PAYLOAD = "dummy payload";
@@ -119,7 +120,7 @@ public void hiddenTargetHandlingAtRoot()
// name should be not garbaged anymore
assertThat( target.getName(), equalTo( "archetype-catalog.xml" ) );
// path prefixed by OS from root, no garbage at tail
- assertThat( target.getPath(), endsWith( "target/repoId/archetype-catalog.xml" ) );
+ assertThat( target.getPath(), endsWith( "target/repoId/archetype-catalog.xml".replace("/", File.separator) ) );
// content is fine too
assertThat( FileUtils.fileRead( target ), equalTo( PAYLOAD ) );
}
View
2  nexus/nexus-oss-webapp/pom.xml
@@ -248,8 +248,6 @@
<excludes>
<!-- FIXME: I hate this too, but other pattern did not help (ie. xmlpull-*.jar) At least build will fail anyway if version changes... -->
<exclude>**/xmlpull-1.1.3.1.jar</exclude>
- <!-- FIXME: Remove once Commons Beanutils 1.8.4 is released and we upgrade to it, see BEANUTILS-379 -->
- <exclude>**/commons-beanutils-core-1.7.0.jar</exclude>
</excludes>
</configuration>
</execution>
View
80 ...s/nexus-oss-webapp/src/main/resources/content/conf/examples/jetty-redirect-access-log.xml
@@ -1,80 +0,0 @@
-<?xml version="1.0"?>
-<!--
-
- Sonatype Nexus (TM) Open Source Version
- Copyright (c) 2007-2012 Sonatype, Inc.
- All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
-
- This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
- which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
-
- Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
- of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
- Eclipse Foundation. All other trademarks are the property of their respective owners.
-
--->
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
- <Set name="threadPool">
- <New class="org.sonatype.sisu.jetty.thread.InstrumentedQueuedThreadPool"/>
- </Set>
- <Call name="addConnector">
- <Arg>
- <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
- <Set name="host">${application-host}</Set>
- <Set name="port">${application-port}</Set>
- </New>
- </Arg>
- </Call>
-
- <New id="MovedHandler" class="org.eclipse.jetty.server.handler.MovedContextHandler">
- <Set name="contextPath">/nexus/content/all</Set>
- <Set name="newContextURL">/nexus/content/public</Set>
- <Set name="permanent">false</Set>
- <Set name="discardPathInfo">false</Set>
- <Set name="discardQuery">false</Set>
-
- </New>
-
- <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
- <!-- The following configuration is REQUIRED, and MUST BE FIRST.·
- It makes the Plexus container available for use in the Nexus webapp. -->
- <Call name="addLifeCycleListener">
- <Arg>
- <New class="org.sonatype.plexus.jetty.custom.InjectExistingPlexusListener" />
- </Arg>
- </Call>
-
- <!-- The following configuration disables JSP taglib support, the validation of which
- slows down Jetty's startup significantly. -->
- <Call name="addLifeCycleListener">
- <Arg>
- <New class="org.sonatype.plexus.jetty.custom.DisableTagLibsListener" />
- </Arg>
- </Call>
- </New>
-
-
- <New id="NexusWebAppContext" class="org.eclipse.jetty.webapp.WebAppContext">
- <Arg><Ref id="Contexts"/></Arg>
- <Arg>${nexus-webapp}</Arg>
- <Arg>${nexus-webapp-context-path}</Arg>
- <Set name="extractWAR">false</Set>
- </New>
-
- <Set name="handler"> <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
- <Set name="handlers">
- <Array type="org.eclipse.jetty.server.Handler">
- <Item><Ref id="MovedHandler"/></Item>
- <Item><Ref id="Contexts"/></Item>
- <Item><New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/></Item>
- </Array>
- </Set>
- </New>
- </Set>
- <Set name="stopAtShutdown">true</Set>
- <Set name="sendServerVersion">true</Set>
- <Set name="sendDateHeader">true</Set>
- <Set name="gracefulShutdown">1000</Set>
-</Configure>
View
126 ...ain/java/org/sonatype/nexus/proxy/storage/remote/httpclient/InterruptableInputStream.java
@@ -0,0 +1,126 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2007-2012 Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+package org.sonatype.nexus.proxy.storage.remote.httpclient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+
+import org.apache.http.client.methods.AbortableHttpRequest;
+
+/**
+ * Best-effort interruptable InputStream wrapper. The wrapper checks for Thread.isInterrupred before delegating to the
+ * actual stream. If the thread is interrupted, the wrapper calls AbortableHttpRequest.abort() and throws
+ * InterruptedIOException.
+ */
+class InterruptableInputStream
+ extends InputStream
+{
+
+ private final InputStream stream;
+
+ private AbortableHttpRequest request;
+
+ public InterruptableInputStream( final AbortableHttpRequest request, final InputStream stream )
+ {
+ this.request = request;
+ this.stream = stream;
+ }
+
+ public InterruptableInputStream( final InputStream stream )
+ {
+ this( null, stream );
+ }
+
+ private void abortIfInterrupted()
+ throws IOException
+ {
+ if ( Thread.interrupted() )
+ {
+ if ( request != null )
+ {
+ request.abort();
+ }
+ throw new InterruptedIOException();
+ }
+ }
+
+ @Override
+ public int read()
+ throws IOException
+ {
+ abortIfInterrupted();
+ return stream.read();
+ }
+
+ @Override
+ public int read( byte[] b )
+ throws IOException
+ {
+ abortIfInterrupted();
+ return stream.read( b );
+ }
+
+ @Override
+ public int read( byte b[], int off, int len )
+ throws IOException
+ {
+ abortIfInterrupted();
+ return stream.read( b, off, len );
+ }
+
+ @Override
+ public long skip( long n )
+ throws IOException
+ {
+ abortIfInterrupted();
+ return stream.skip( n );
+ }
+
+ @Override
+ public int available()
+ throws IOException
+ {
+ abortIfInterrupted();
+ return stream.available();
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ // do not throw InterruptedIOException here!
+ // this will not close the stream and likely mask original exception!
+ stream.close();
+ }
+
+ @Override
+ public void mark( int readlimit )
+ {
+ stream.mark( readlimit );
+ }
+
+ @Override
+ public void reset()
+ throws IOException
+ {
+ abortIfInterrupted();
+ stream.reset();
+ }
+
+ @Override
+ public boolean markSupported()
+ {
+ return stream.markSupported();
+ }
+}
View
1  nexus/nexus-test/nexus-test-harness-its/resources/nexus570/projects/simple-archetype/pom.xml
@@ -4,6 +4,7 @@
<groupId>nexus570</groupId>
<artifactId>simple-archetype</artifactId>
<version>3.0.1</version>
+ <packaging>maven-archetype</packaging>
<distributionManagement>
<repository>
<id>nexus-test-harness-repo</id>
View
38 .../src/test/java/org/sonatype/nexus/integrationtests/nexus570/Nexus570IndexArchetypeIT.java
@@ -32,6 +32,41 @@ public void setSecureTest() {
@Test
public void searchForArchetype() throws Exception {
+ // Short explanation what (might) happen here
+ // 1) for start, indexing of deployed stuff is done async
+ // 2) second, superclass of this IT deploys using
+ // deployArtifacts( File project, String wagonHint, String deployUrl, Model model ) method
+ // that does not wait for async event handlers to calm down, but is important to
+ // state that deploy happens in "maven order", POM then JAR (will become important later)
+ // 3) third, the "simple-archetype" being deployed has POM that does not state packaging,
+ // hence, default "jar" packaging is assumed (this is maven "rule").
+ // 4) fourth, the JAR that is deployed 2nd, does have HETA-INF/archetype.xml, present in archetypes only
+ // 5) fifth, due to "known problems" for problem "how to detect is an artifact and archetype or not",
+ // maven indexer contains a "bit" of heuristics. Maven Archetypes were recognized in many ways,
+ // but until recently they had no proper "packaging" set to "maven-archetype". Older archetypes
+ // had JAR packaging in POM and only the aforementioned XML was telling they are archetypes and not "plain JARs"
+ // actually. Also, there is a mess with "partial archetypes", etc.
+ // 6) Finally, sixth, as Nexus receives deploys (POM then JAR), it will kick indexer at every of them
+ // but the actually indexed data will "incrementally" become correct: at POM deploy JAR will be not
+ // present, so Indexer will create "partial" context based on POM only (hence, packaging will be JAR
+ // and for example no classnames will be indexed due to the lack of JAR). On 2nd deploy of JAR,
+ // the created context will be complete (as now bot POM and JAR will be present), and packaging
+ // will be properly set to "maven-archetype".
+ //
+ // See the "heuristic" here (MI 5.1.0):
+ // https://github.com/apache/maven-indexer/blob/91147a9311b7aef2dce50e581f4fafad710486f3/indexer-core/src/main/java/org/apache/maven/index/creator/MavenArchetypeArtifactInfoIndexCreator.java#L37
+ // https://github.com/apache/maven-indexer/blob/91147a9311b7aef2dce50e581f4fafad710486f3/indexer-core/src/main/java/org/apache/maven/index/creator/MinimalArtifactInfoIndexCreator.java#L50
+ //
+ // So what happened, is that the assertion below got a "hit" from Indexer, but the hit was actually
+ // returned from "partially" indexed artifact, and due to JAR not yet indexed, Maven Indexer
+ // did it's best: it assumed packaging is JAR, the information told by POM.
+ //
+ // Solution(s):
+ // a) added wait for calm down (should make everything work more reliable even without b))
+ // b) added proper packaging to POM of the resource of this IT
+ //
+
+ getEventInspectorsUtil().waitForCalmPeriod();
Map<String, String> args = new HashMap<String, String>();
args.put("a", "simple-archetype");
args.put("g", "nexus570");
@@ -47,6 +82,9 @@ public void searchForArchetype() throws Exception {
@Test
public void searchForjar() throws Exception {
+ // unneeded here, as only POM will do for assertion to succeed
+ // see above full explanation
+ getEventInspectorsUtil().waitForCalmPeriod();
Map<String, String> args = new HashMap<String, String>();
args.put("a", "normal");
args.put("g", "nexus570");
View
11 ...ncher/src/main/java/org/sonatype/nexus/integrationtests/AbstractNexusIntegrationTest.java
@@ -680,14 +680,21 @@ protected void deployArtifacts( File project, String wagonHint, String deployUrl
File pom = new File( project, "pom.xml" );
// FIXME, this needs to be fluffed up a little, should add the classifier, etc.
- String artifactFileName = model.getArtifactId() + "." + model.getPackaging();
+ String extension = model.getPackaging();
+ // for now, only due to Nexus570IndexArchetypeIT
+ // no other IT specifies other packaging where extension != packaging
+ if ( "maven-archetype".equals( extension ) )
+ {
+ extension = "jar";
+ }
+ String artifactFileName = model.getArtifactId() + "." + extension;
File artifactFile = new File( project, artifactFileName );
log.debug( "wow, this is working: " + artifactFile.getName() );
final Gav gav =
new Gav( model.getGroupId(), model.getArtifactId(), model.getVersion(), null,
- FileUtils.getExtension( artifactFile.getName() ), null, null, artifactFile.getName(), false, null,
+ extension, null, null, artifactFile.getName(), false, null,
false, null );
// the Restlet Client does not support multipart forms:
View
21 ...-testsuite-support/src/main/java/org/sonatype/nexus/testsuite/support/NexusITSupport.java
@@ -444,15 +444,22 @@ protected NexusClient createNexusClientForAnonymous( final NexusBundle nexus )
*/
protected void logRemoteThatTestIs( final Logger remoteLogger, final String doingWhat )
{
- final String message = "TEST " + testName.getMethodName() + " " + doingWhat;
+ try
+ {
+ final String message = "TEST " + testName.getMethodName() + " " + doingWhat;
- final StringBuilder fullMessage = new StringBuilder()
- .append( "\n" )
- .append( StringUtils.repeat( "*", message.length() ) ).append( "\n" )
- .append( message ).append( "\n" )
- .append( StringUtils.repeat( "*", message.length() ) );
+ final StringBuilder fullMessage = new StringBuilder()
+ .append( "\n" )
+ .append( StringUtils.repeat( "*", message.length() ) ).append( "\n" )
+ .append( message ).append( "\n" )
+ .append( StringUtils.repeat( "*", message.length() ) );
- remoteLogger.info( fullMessage.toString() );
+ remoteLogger.info( fullMessage.toString() );
+ }
+ catch ( final Exception e )
+ {
+ logger.warn( "Failed to log remote that test was '{}' ({})", doingWhat, e.getMessage() );
+ }
}
/**
View
9 ...ite-support/src/main/java/org/sonatype/nexus/testsuite/support/NexusRunningITSupport.java
@@ -236,14 +236,7 @@ protected Logger remoteLogger()
*/
private void logRemoteThatTestIs( final String doingWhat )
{
- try
- {
- logRemoteThatTestIs( remoteLogger(), doingWhat );
- }
- catch ( final Exception e )
- {
- logger.warn( "Failed to log remote that test was '{}' ({})", doingWhat, e.getMessage() );
- }
+ logRemoteThatTestIs( remoteLogger(), doingWhat );
}
private void startNexus( final NexusBundle nexusBundle )
View
41 nexus/nexus-webapp/src/main/webapp/js/repoServer/RoleEditPanel.js
@@ -64,16 +64,7 @@ Sonatype.repoServer.RoleEditPanel = function(config) {
}
}
- /* ext-3.4.0
- if (this.sp.checkPermission('security:roles', this.sp.CREATE) && store.getCount() > 0 && this.toolbarAddButton.menu.items.length == 1)
- {
- this.toolbarAddButton.menu.add({
- text : 'External Role Mapping',
- handler : this.mapExternalRoles,
- scope : this
- });
- }
- */
+ this.userLocatorsLoaded = true;
},
scope : this
}
@@ -168,13 +159,29 @@ Ext.extend(Sonatype.repoServer.RoleEditPanel, Sonatype.panels.GridViewer, {
scope : this
});
}
- /* ext 3.4.0 */
- if (this.sp.checkPermission('security:roles', this.sp.CREATE) && this.sourceStore.getCount() > 0 ) {
- this.toolbarAddButton.menu.add({
- text : 'External Role Mapping',
- handler : this.mapExternalRoles,
- scope : this
- });
+
+ var
+ self = this,
+ addExternalMappingMenuItem = function() {
+ if (self.sp.checkPermission('security:roles', self.sp.CREATE) && self.sourceStore.getCount() > 0 ) {
+ self.toolbarAddButton.menu.add({
+ text : 'External Role Mapping',
+ handler : self.mapExternalRoles,
+ scope : self
+ });
+
+ self.externalMappingMenuItemAdded = true;
+ }
+ };
+
+ if (self.externalMappingMenuItemAdded) {
+ return;
+ }
+
+ if (self.userLocatorsLoaded) {
+ addExternalMappingMenuItem();
+ } else {
+ this.sourceStore.on('load', addExternalMappingMenuItem, this, {single : true});
}
},
View
29 ...r-lucene-plugin/src/main/java/org/sonatype/nexus/events/IndexerManagerEventInspector.java
@@ -22,6 +22,7 @@
import org.sonatype.nexus.proxy.events.RepositoryItemEventCache;
import org.sonatype.nexus.proxy.events.RepositoryItemEventDelete;
import org.sonatype.nexus.proxy.events.RepositoryItemEventStore;
+import org.sonatype.nexus.proxy.repository.Repository;
import org.sonatype.nexus.util.SystemPropertiesHelper;
import org.sonatype.plexus.appevents.Event;
@@ -35,8 +36,8 @@
extends AbstractEventInspector
implements AsynchronousEventInspector
{
- private final boolean enabled = SystemPropertiesHelper.getBoolean(
- "org.sonatype.nexus.events.IndexerManagerEventInspector.enabled", true );
+ private final boolean enabled =
+ SystemPropertiesHelper.getBoolean( "org.sonatype.nexus.events.IndexerManagerEventInspector.enabled", true );
@Requirement
private IndexerManager indexerManager;
@@ -63,26 +64,28 @@ public void inspect( Event<?> evt )
private void inspectForIndexerManager( Event<?> evt )
{
- try
- {
- RepositoryItemEvent ievt = (RepositoryItemEvent) evt;
+ RepositoryItemEvent ievt = (RepositoryItemEvent) evt;
+
+ Repository repository = ievt.getRepository();
- // should we sync at all
- if ( ievt.getRepository().isIndexable() )
+ // should we sync at all
+ if ( repository != null && repository.isIndexable() )
+ {
+ try
{
if ( ievt instanceof RepositoryItemEventCache || ievt instanceof RepositoryItemEventStore )
{
- getIndexerManager().addItemToIndex( ievt.getRepository(), ievt.getItem() );
+ getIndexerManager().addItemToIndex( repository, ievt.getItem() );
}
else if ( ievt instanceof RepositoryItemEventDelete )
{
- getIndexerManager().removeItemFromIndex( ievt.getRepository(), ievt.getItem() );
+ getIndexerManager().removeItemFromIndex( repository, ievt.getItem() );
}
}
- }
- catch ( Exception e ) // TODO be more specific
- {
- getLogger().error( "Could not maintain index!", e );
+ catch ( Exception e ) // TODO be more specific
+ {
+ getLogger().error( "Could not maintain index for repository {}!", repository.getId(), e );
+ }
}
}
View
109 ...s-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/index/DefaultIndexerManager.java
@@ -30,10 +30,11 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nullable;
@@ -316,7 +317,13 @@ private boolean ISGROUP(Repository repository)
* Note that locks are only added to the map, never removed. This introduces a minor memory leak for each deleted
* repository id, but makes synchronization logic much easier.
*/
- private final Map<String, ReentrantLock> reindexLocks = new HashMap<String, ReentrantLock>();
+ private final Map<String, ForceableReentrantLock> reindexLocks = new HashMap<String, ForceableReentrantLock>();
+
+ /**
+ * Threads attempting to delete repository indexing contexts. Used as a marker to index requests for repositories
+ * that are being deleted.
+ */
+ private final ConcurrentMap<String, Thread> deleteThreads = new ConcurrentHashMap<String, Thread>();
private File workingDirectory;
@@ -432,10 +439,10 @@ private void addRepositoryIndexContext( final Repository repository, IndexingCon
if ( oldContext != null )
{
- // this is really an error, the oldContext is expected to be null here
- mavenIndexer.removeIndexingContext( oldContext, true );
- logger.warn( "Removed old/stale indexing context {} for repository {}", oldContext.getId(),
- repository.getId() );
+ // this is an error, oldContext can have filesystem locks or long-running threads
+ logger.error( "Old/stale indexing context {} for repository {}. Operation cancaled.", oldContext.getId(),
+ repository.getId() );
+ return;
}
IndexingContext ctx = null;
@@ -488,27 +495,65 @@ public void removeRepositoryIndexContext( String repositoryId, final boolean del
public void removeRepositoryIndexContext( final Repository repository, final boolean deleteFiles )
throws IOException
{
- exclusiveSingle( repository, new Runnable()
+ Thread otherThread = deleteThreads.putIfAbsent( repository.getId(), Thread.currentThread() );
+ if ( otherThread != null )
{
- @Override
- public void run( final IndexingContext context )
- throws IOException
+ logger.debug( "Indexing context for repository {} is being deleted by thread {}", repository.getId(),
+ otherThread.getName() );
+ return;
+ }
+
+ try
+ {
+ final boolean[] removed = new boolean[1];
+ final ForceableReentrantLock lock = getReindexLock( repository );
+ if ( lock.tryForceLock( lockTimeoutSeconds, TimeUnit.SECONDS ) )
{
- if ( context != null )
+ try
{
- logger.debug( "Removing indexing context for repository {} deleteFiles={}", repository.getId(),
- deleteFiles );
-
- mavenIndexer.removeIndexingContext( context, deleteFiles );
-
- logger.debug( "Removed indexing context {} for repository {}", context.getId(), repository.getId() );
+ exclusiveSingle( repository, new Runnable()
+ {
+ @Override
+ public void run( final IndexingContext context )
+ throws IOException
+ {
+ removeRepositoryIndexingContext( repository, deleteFiles, context );
+ removed[0] = true;
+ }
+ } );
}
- else
+ finally
{
- logger.debug( "Could not remove <null> indexing context for repository {}", repository.getId() );
+ lock.unlock();
}
}
- } );
+ if ( !removed[0] )
+ {
+ throw new IOException( "Could not remove indexing context for repository " + repository.getId() );
+ }
+ }
+ finally
+ {
+ deleteThreads.remove( repository.getId() );
+ }
+ }
+
+ private void removeRepositoryIndexingContext( final Repository repository, final boolean deleteFiles,
+ final IndexingContext context )
+ throws IOException
+ {
+ if ( context != null )
+ {
+ logger.debug( "Removing indexing context for repository {} deleteFiles={}", repository.getId(), deleteFiles );
+
+ mavenIndexer.removeIndexingContext( context, deleteFiles );
+
+ logger.debug( "Removed indexing context {} for repository {}", context.getId(), repository.getId() );
+ }
+ else
+ {
+ logger.debug( "Could not remove <null> indexing context for repository {}", repository.getId() );
+ }
}
public void updateRepositoryIndexContext( final String repositoryId )
@@ -956,7 +1001,7 @@ private void reindexRepository( final Repository repository, final String fromPa
return;
}
- Lock reindexLock = getReindexLock( repository );
+ ForceableReentrantLock reindexLock = getReindexLock( repository );
if ( reindexLock.tryLock() )
{
try
@@ -1092,7 +1137,7 @@ protected void downloadRepositoryIndex( final ProxyRepository repository, final
return;
}
- Lock reindexLock = getReindexLock( repository );
+ ForceableReentrantLock reindexLock = getReindexLock( repository );
if ( reindexLock.tryLock() )
{
try
@@ -1379,7 +1424,7 @@ protected void publishRepositoryIndex( final Repository repository )
return;
}
- Lock reindexLock = getReindexLock( repository );
+ ForceableReentrantLock reindexLock = getReindexLock( repository );
if ( reindexLock.tryLock() )
{
try
@@ -2505,6 +2550,16 @@ private void exclusiveSingle( Repository repository, Runnable runnable )
*/
private Lock getRepositoryLock( Repository repository, boolean exclusive )
{
+ final String lockName = exclusive ? "exclusive" : "shared";
+
+ Thread deleteThread = deleteThreads.get( repository.getId() );
+ if ( deleteThread != null && deleteThread != Thread.currentThread() )
+ {
+ logger.debug( "Could not acquire {} lock on repository {}. The repository is being deleted by thread {}.",
+ lockName, repository.getId(), deleteThread.getName() );
+ return null;
+ }
+
ReadWriteLock rwlock;
synchronized ( repositoryLocks )
{
@@ -2515,7 +2570,7 @@ private Lock getRepositoryLock( Repository repository, boolean exclusive )
repositoryLocks.put( repository.getId(), rwlock );
}
}
- final String lockName = exclusive ? "exclusive" : "shared";
+
try
{
Lock lock = exclusive ? rwlock.writeLock() : rwlock.readLock();
@@ -2551,14 +2606,14 @@ private Lock getRepositoryLock( Repository repository, boolean exclusive )
* Returns "reindex" reentrant lock that corresponds to the repository. The lock is used to protect access to
* repository gz index download and publishing areas and to the repository local storage.
*/
- private ReentrantLock getReindexLock( final Repository repository )
+ private ForceableReentrantLock getReindexLock( final Repository repository )
{
synchronized ( reindexLocks )
{
- ReentrantLock lock = reindexLocks.get( repository.getId() );
+ ForceableReentrantLock lock = reindexLocks.get( repository.getId() );
if ( lock == null )
{
- lock = new ReentrantLock();
+ lock = new ForceableReentrantLock();
reindexLocks.put( repository.getId(), lock );
}
return lock;
View
94 ...-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/index/ForceableReentrantLock.java
@@ -0,0 +1,94 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2007-2012 Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+package org.sonatype.nexus.index;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Simple high-contention low-traffic reentrant lock implementation. In addition to regular #tryLock/#unlock methods,
+ * provides #tryForceLock that attempts to force lock acquisition by periodically calling #interrupt on the thread
+ * holding the lock.
+ * <p>
+ * This class is not meant as general purpose lock implementation. It is specifically tailored for synchronizing
+ * repository index download, reindex, publish and delete operations.
+ */
+class ForceableReentrantLock
+{
+ private final Object lock = new Object();
+
+ private Thread owner;
+
+ private int count;
+
+ public boolean tryLock()
+ {
+ synchronized ( lock )
+ {
+ if ( owner != null && owner != Thread.currentThread() )
+ {
+ return false;
+ }
+ owner = Thread.currentThread();
+ count++;
+ return true;
+ }
+ }
+
+ public void unlock()
+ {
+ synchronized ( lock )
+ {
+ if ( owner != Thread.currentThread() )
+ {
+ throw new IllegalStateException();
+ }
+ if ( --count == 0 )
+ {
+ owner = null;
+ lock.notifyAll();
+ }
+ }
+ }
+
+ public boolean tryForceLock( long time, TimeUnit unit )
+ {
+ final long start = System.currentTimeMillis();
+ synchronized ( lock )
+ {
+ while ( owner != null && owner != Thread.currentThread() )
+ {
+ if ( timeout( start, time, unit ) )
+ {
+ return false;
+ }
+ owner.interrupt();
+ try
+ {
+ lock.wait( 1000L );
+ }
+ catch ( InterruptedException e )
+ {
+ return false;
+ }
+ }
+ owner = Thread.currentThread();
+ count++;
+ return true;
+ }
+ }
+
+ private boolean timeout( long start, long duration, TimeUnit unit )
+ {
+ return System.currentTimeMillis() - start > unit.toMillis( duration );
+ }
+}
View
12 nexus/plugins/indexer/nexus-indexer-lucene-plugin/src/main/js/repoServer.SearchResultGrid.js
@@ -266,8 +266,16 @@ Ext.extend(Sonatype.repoServer.SearchResultGrid, Ext.grid.GridPanel, {
latest = record.get('latestSnapshot');
}
- return '<a href="#nexus-search;gav~' + record.get('groupId') + '~' + record.get('artifactId')
- + '~~~~kw,versionexpand " onmousedown="cancel_bubble(event)" onclick="cancel_bubble(event); return true;">Show All Versions</a>';
+ var linkMarkup = '<a href="#nexus-search;gav~' + record.get('groupId') + '~' + record.get('artifactId')
+ + '~~~~kw,versionexpand " onmousedown="cancel_bubble(event)" onclick="cancel_bubble(event); return true;">';
+
+ if (store.reader.jsonData.tooManyResults) {
+ return linkMarkup + 'Show All Versions</a>';
+ } else {
+ return 'Latest: ' + latest + ' ' + linkMarkup + '(Show All Versions)</a>';
+
+ }
+
},
formatDownloadLinks : function(value, p, record, rowIndex, colIndex, store) {
var hitIndex = 0;
View
26 ...indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/DefaultIndexerManagerIT.java
@@ -14,12 +14,16 @@
import java.util.Collection;
+import junit.framework.Assert;
+
import org.apache.maven.index.ArtifactInfo;
import org.apache.maven.index.IteratorSearchResponse;
import org.apache.maven.index.MAVEN;
+import org.apache.maven.index.context.IndexingContext;
import org.junit.Test;
import org.sonatype.nexus.Nexus;
import org.sonatype.nexus.proxy.RemoteStorageException;
+import org.sonatype.nexus.proxy.maven.MavenProxyRepository;
import org.sonatype.nexus.proxy.repository.ProxyRepository;
import org.sonatype.nexus.templates.repository.maven.Maven2ProxyRepositoryTemplate;
@@ -85,7 +89,8 @@ public void testRepoSha1Search()
// org.sonatype.nexus : nexus-indexer : 1.0-beta-4
// sha1: 86e12071021fa0be4ec809d4d2e08f07b80d4877
- Collection<ArtifactInfo> ais = indexerManager.identifyArtifact( MAVEN.SHA1, "86e12071021fa0be4ec809d4d2e08f07b80d4877" );
+ Collection<ArtifactInfo> ais =
+ indexerManager.identifyArtifact( MAVEN.SHA1, "86e12071021fa0be4ec809d4d2e08f07b80d4877" );
assertTrue( "The artifact has to be found!", ais.size() == 1 );
@@ -94,7 +99,7 @@ public void testRepoSha1Search()
// this will be EXACT search, since we gave full SHA1 checksum of 40 chars
response =
indexerManager.searchArtifactSha1ChecksumIterator( "86e12071021fa0be4ec809d4d2e08f07b80d4877", null, null,
- null, null, null );
+ null, null, null );
assertEquals( "There should be one hit!", 1, response.getTotalHits() );
@@ -122,4 +127,21 @@ public void testInvalidRemoteUrl()
indexerManager.reindexRepository( "/", r.getId(), true );
}
+
+ @Test
+ public void testDuplicateAddRepositoryRequest()
+ throws Exception
+ {
+ MavenProxyRepository repo = central;
+
+ IndexingContext repoCtx = indexerManager.getRepositoryIndexContext( repo );
+
+ Assert.assertNotNull( repoCtx );
+
+ indexerManager.addRepositoryIndexContext( repo );
+
+ IndexingContext repoCtx2 = indexerManager.getRepositoryIndexContext( repo );
+
+ Assert.assertSame( repoCtx, repoCtx2 );
+ }
}
View
150 ...lugin/src/test/java/org/sonatype/nexus/index/DeleteRepositoryWhileDownloadingIndexIT.java
@@ -0,0 +1,150 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2007-2012 Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+package org.sonatype.nexus.index;
+
+import java.util.concurrent.Semaphore;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.powermock.reflect.Whitebox;
+import org.sonatype.nexus.ApplicationStatusSource;
+import org.sonatype.nexus.mime.MimeSupport;
+import org.sonatype.nexus.proxy.ItemNotFoundException;
+import org.sonatype.nexus.proxy.RemoteAccessException;
+import org.sonatype.nexus.proxy.RemoteStorageException;
+import org.sonatype.nexus.proxy.ResourceStoreRequest;
+import org.sonatype.nexus.proxy.item.AbstractStorageItem;
+import org.sonatype.nexus.proxy.item.StorageItem;
+import org.sonatype.nexus.proxy.maven.RepositoryPolicy;
+import org.sonatype.nexus.proxy.repository.ProxyRepository;
+import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException;
+import org.sonatype.nexus.proxy.storage.remote.AbstractRemoteRepositoryStorage;
+import org.sonatype.nexus.proxy.storage.remote.RemoteRepositoryStorage;
+import org.sonatype.nexus.proxy.storage.remote.RemoteStorageContext;
+import org.sonatype.nexus.proxy.utils.UserAgentBuilder;
+
+public class DeleteRepositoryWhileDownloadingIndexIT
+ extends AbstractIndexerManagerTest
+{
+ private Semaphore semaphore = new Semaphore( 1 );
+
+ private final class StuckRemoteStorage
+ extends AbstractRemoteRepositoryStorage
+ implements RemoteRepositoryStorage
+ {
+ public volatile boolean interrupted;
+
+ protected StuckRemoteStorage()
+ throws Exception
+ {
+ super( lookup( UserAgentBuilder.class ), lookup( ApplicationStatusSource.class ),
+ lookup( MimeSupport.class ) );
+ }
+
+ @Override
+ public AbstractStorageItem retrieveItem( ProxyRepository repository, ResourceStoreRequest request,
+ String baseUrl )
+ throws ItemNotFoundException, RemoteAccessException, RemoteStorageException
+ {
+ try
+ {
+ semaphore.release();
+ Thread.sleep( 120 * 1000L );
+ }
+ catch ( InterruptedException e )
+ {
+ interrupted = true;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProviderId()
+ {
+ return "test";
+ }
+
+ @Override
+ public boolean isReachable( ProxyRepository repository, ResourceStoreRequest request )
+ throws RemoteAccessException, RemoteStorageException
+ {
+ return true;
+ }
+
+ @Override
+ public void validateStorageUrl( String url )
+ throws RemoteStorageException
+ {
+ }
+
+ @Override
+ public boolean containsItem( long newerThen, ProxyRepository repository, ResourceStoreRequest request )
+ throws RemoteAccessException, RemoteStorageException
+ {
+ return true;
+ }
+
+ @Override
+ public void storeItem( ProxyRepository repository, StorageItem item )
+ throws UnsupportedStorageOperationException, RemoteAccessException, RemoteStorageException
+ {
+ }
+
+ @Override
+ public void deleteItem( ProxyRepository repository, ResourceStoreRequest request )
+ throws ItemNotFoundException, UnsupportedStorageOperationException, RemoteAccessException,
+ RemoteStorageException
+ {
+ }
+
+ @Override
+ protected void updateContext( ProxyRepository repository, RemoteStorageContext context )
+ throws RemoteStorageException
+ {
+ }
+
+ }
+
+ @Test
+ public void testDeleteRepository()
+ throws Exception
+ {
+ /*
+ * The point of this test is to verify that repository indexing context can be removed when there is remote
+ * index download in progress for the repository.
+ */
+
+ semaphore.acquire();
+
+ central.setDownloadRemoteIndexes( true );
+ central.setRemoteUrl( "http://localhost:" + 12345 );
+ central.setRepositoryPolicy( RepositoryPolicy.SNAPSHOT );
+
+ nexusConfiguration.saveConfiguration();
+
+ // central.setRemoteStorage( new StuckRemoteStorage() );
+ StuckRemoteStorage stuckRemoteStorage = new StuckRemoteStorage();
+ Whitebox.setInternalState( central, "remoteStorage", stuckRemoteStorage );
+
+ Assert.assertTrue( central.getRemoteStorage() instanceof StuckRemoteStorage );
+
+ // wait until download is stuck
+ semaphore.acquire();
+
+ // this is expected to interrupt index download thread and remove the indexing context
+ indexerManager.removeRepositoryIndexContext( central, true );
+
+ Assert.assertNull( indexerManager.getRepositoryIndexContext( central ) );
+ Assert.assertTrue( stuckRemoteStorage.interrupted );
+ }
+}
View
128 ...nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/ForceableLockTest.java
@@ -0,0 +1,128 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2007-2012 Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+package org.sonatype.nexus.index;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ForceableLockTest
+{
+ private ForceableReentrantLock subject;
+
+ private TestThread otherThread;
+
+ private static class TestThread
+ extends Thread
+ {
+ public volatile boolean interrupted = false;
+
+ public volatile boolean locked = false;
+
+ public final Semaphore semaphore = new Semaphore( 1 );
+
+ private final ForceableReentrantLock lock;
+
+ public TestThread( final ForceableReentrantLock lock )
+ {
+ this.lock = lock;
+ }
+
+ @Override
+ public void run()
+ {
+ locked = lock.tryLock();
+ semaphore.release();
+ try
+ {
+ try
+ {
+ Thread.sleep( 60 * 1000L );
+ }
+ catch ( InterruptedException e )
+ {
+ interrupted = true;
+ }
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ }
+
+ @Before
+ public void setUp()
+ {
+ subject = new ForceableReentrantLock();
+ otherThread = new TestThread( subject );
+ }
+
+ @After
+ public void tearDown()
+ throws InterruptedException
+ {
+ otherThread.interrupt();
+ otherThread.join();
+ }
+
+ @Test
+ public void basic()
+ throws InterruptedException
+ {
+ otherThread.semaphore.acquire();
+ otherThread.start();
+ otherThread.semaphore.acquire();
+
+ Assert.assertTrue( otherThread.locked ); // the other thread is holding the lock
+ Assert.assertFalse( otherThread.interrupted ); // sanity check
+ Assert.assertFalse( subject.tryLock() ); // this thread is not able to get the lock
+
+ Assert.assertTrue( subject.tryForceLock( 5, TimeUnit.SECONDS ) ); // this thread can force the lock
+ Assert.assertTrue( otherThread.interrupted );
+ }
+
+ @Test
+ public void reentrant()
+ {
+ Assert.assertTrue( subject.tryLock() );
+ Assert.assertTrue( subject.tryLock() );
+
+ subject.unlock();
+ subject.unlock();
+ }
+
+ @Test( expected = IllegalStateException.class )
+ public void nonownerUnlock()
+ throws InterruptedException
+ {
+ otherThread.semaphore.acquire();
+ otherThread.start();
+ otherThread.semaphore.acquire();
+
+ Assert.assertTrue( otherThread.locked ); // the other thread is holding the lock
+ Assert.assertFalse( otherThread.interrupted ); // sanity check
+
+ subject.unlock();
+ }
+
+ @Test( expected = IllegalStateException.class )
+ public void notlockedUnlock()
+ {
+ subject.unlock();
+ }
+}
View
114 .../nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/IndexingLockTest.java
@@ -1,114 +0,0 @@
-/*
- * Sonatype Nexus (TM) Open Source Version
- * Copyright (c) 2007-2012 Sonatype, Inc.
- * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
- *
- * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
- * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
- *
- * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
- * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
- * Eclipse Foundation. All other trademarks are the property of their respective owners.
- */
-package org.sonatype.nexus.index;
-
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class IndexingLockTest
-{
-
- private ReadWriteLock locker;
-
- @Before
- public void setUp()
- throws Exception
- {
- locker = new ReentrantReadWriteLock();
- }
-
- @Test
- public void testIndexLocking()
- {
- beginIndex();
- }
-
- @Test
- public void testThreadIndexLocking() throws InterruptedException
- {
- new Thread( new Runnable()
- {
-
- public void run()
- {
- Lock lock = locker.writeLock();
- lock.lock();
-
- Thread.yield();
-
- try
- {
- Thread.sleep( 3000 );
- }
- catch ( InterruptedException e )
- {
- // no problem
- }
- finally
- {
- lock.unlock();
- }
- }
- } ).start();
-
- Thread.yield();
- Thread.sleep( 100 );
- Thread.yield();
-
- Lock lock = locker.readLock();
- boolean hasLock = true;
- try
- {
- hasLock = lock.tryLock();
- Assert.assertFalse( hasLock );
- }
- finally
- {
- if ( hasLock )
- lock.unlock();
- }
- }
-
- private void beginIndex()
- {
- Lock lock = locker.writeLock();
- lock.lock();
- try
- {
- downloadRemoteIndex();
- }
- finally
- {
- lock.unlock();
- }
- }
-
- private void downloadRemoteIndex()
- {
- Lock lock = locker.readLock();
- try
- {
- Assert.assertTrue( lock.tryLock() );
- }
- finally
- {
- lock.unlock();
- }
- }
-
-}
View
2  ...nexus-indexer-lucene-plugin/src/test/java/org/sonatype/nexus/index/RepositoryStateIT.java
@@ -22,8 +22,6 @@
import org.apache.lucene.search.Query;
import org.apache.maven.index.ArtifactInfo;
import org.apache.maven.index.Field;
-import org.apache.maven.index.FlatSearchRequest;
-import org.apache.maven.index.FlatSearchResponse;
import org.apache.maven.index.IteratorSearchResponse;
import org.apache.maven.index.MAVEN;
import org.apache.maven.index.NexusIndexer;
View
14 pom.xml
@@ -174,7 +174,6 @@
<slf4j.version>1.7.2</slf4j.version>
<logback.version>1.0.7</logback.version>
<aether.version>1.13.1</aether.version>
- <commons-beanutils.version>1.7.0</commons-beanutils.version>
<enunciate.version>1.26.1</enunciate.version>
<jetty.version>8.1.8.v20121106</jetty.version>
<maven.version>3.0.4</maven.version>
@@ -554,10 +553,15 @@
</exclusions>
</dependency>
+ <!--
+ Use our custom build of commons-beanutils 1.8.3 which excludes the commons-collections classes.
+ For more details see:
+ https://issues.sonatype.org/browse/NEXUS-5336
+ -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils-core</artifactId>
- <version>${commons-beanutils.version}</version>
+ <version>1.8.3-SONATYPE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -567,12 +571,6 @@
</dependency>
<dependency>
- <groupId>commons-beanutils</groupId>
- <artifactId>commons-beanutils-bean-collections</artifactId>
- <version>${commons-beanutils.version}</version>
- </dependency>
-
- <dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.1</version>
Please sign in to comment.
Something went wrong with that request. Please try again.