Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into NXCM-5181-allowForceUpdateOfSystemStatus

  • Loading branch information...
commit f11fba989a5ff8d4017bb98b4846111a9d877178 2 parents 52221aa + 46b1f8d
Kelly Robinson kellyrob99 authored
Showing with 1,609 additions and 804 deletions.
  1. +85 −0 nexus-client-core/src/main/java/org/sonatype/nexus/client/internal/util/Version.java
  2. +9 −2 nexus-client-core/src/main/java/org/sonatype/nexus/client/rest/jersey/JerseyNexusClientFactory.java
  3. +2 −0  ...-core/src/test/filtered-resources/META-INF/maven/org.sonatype.nexus.client/nexus-client-core/pom.properties
  4. +64 −0 nexus-client-core/src/test/java/org/sonatype/nexus/client/rest/jersey/JerseyNexusTestClientUserAgentTest.java
  5. +57 −0 nexus-core/src/main/java/org/sonatype/nexus/guice/AbstractInterceptorModule.java
  6. +2 −3 nexus-core/src/main/java/org/sonatype/nexus/guice/NexusModules.java
  7. +11 −6 nexus-core/src/main/java/org/sonatype/nexus/plugins/DefaultNexusPluginManager.java
  8. +8 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/AbstractChecksumContentValidator.java
  9. +1 −1  nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumContentValidator.java
  10. +18 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumPolicy.java
  11. +4 −3 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperation.java
  12. +7 −6 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/MetadataUtil.java
  13. +12 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/FilePrefixSource.java
  14. +37 −3 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategy.java
  15. +12 −8 nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSLocalRepositoryStorage.java
  16. +2 −3 nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSPeer.java
  17. +5 −1 nexus-core/src/main/java/org/sonatype/nexus/scheduling/AbstractNexusRepositoriesTask.java
  18. +6 −2 nexus-core/src/main/java/org/sonatype/nexus/timing/TimingModule.java
  19. +4 −1 nexus-core/src/test/java/org/sonatype/nexus/plugins/DefaultNexusPluginManagerTest.java
  20. +104 −0 nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperationTest.java
  21. +44 −0 nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategyTest.java
  22. +216 −0 ...e/src/test/java/org/sonatype/nexus/proxy/storage/local/fs/NEXUS5612DefaultFSLocalRepositoryStorageTest.java
  23. +290 −0 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/AbstractMirrorPanel.js
  24. +144 −0 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/HostedMirrorEditor.js
  25. +172 −0 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/Maven2InformationPanel.js
  26. +178 −0 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/ProxyMirrorEditor.js
  27. +12 −11 nexus-webapp/src/main/webapp/js/repoServer.js
  28. +0 −176 nexus-webapp/src/main/webapp/js/repoServer/Maven2InformationPanel.js
  29. +0 −558 nexus-webapp/src/main/webapp/js/repoServer/MirrorConfigPanel.js
  30. +1 −1  plugins/indexer/nexus-indexer-lucene-plugin/pom.xml
  31. +12 −0 plugins/indexer/nexus-indexer-lucene-plugin/src/main/java/org/sonatype/nexus/index/DefaultIndexerManager.java
  32. +8 −1 plugins/nexus-rrb-plugin/src/main/java/org/sonatype/nexus/plugins/rrb/MavenRepositoryReader.java
  33. +20 −9 plugins/nexus-timeline-plugin/src/main/java/org/sonatype/timeline/internal/DefaultTimeline.java
  34. +61 −8 plugins/nexus-timeline-plugin/src/main/java/org/sonatype/timeline/internal/DefaultTimelineIndexer.java
  35. +1 −1  plugins/restlet1x/nexus-restlet1x-plugin/src/main/resources/templates/index.vm
85 nexus-client-core/src/main/java/org/sonatype/nexus/client/internal/util/Version.java
View
@@ -0,0 +1,85 @@
+/*
+ * 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.client.internal.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.io.Closeables;
+
+/**
+ * Helper static methods for reading Maven properties file to get the version from it.
+ *
+ * @author cstamas
+ * @since 2.5.0
+ */
+public class Version
+{
+ private static final Logger LOG = LoggerFactory.getLogger( Version.class );
+
+ private Version()
+ {
+ // no instances
+ }
+
+ /**
+ * Reads the version from a properties file, the one embedded by Maven into Jar during build.
+ *
+ * @param cl the class loader to be used to load the properties file.
+ * @param path the binary path of the properties file to read from (might have more in case of shaded JAR).
+ * @param defaultVersion the version string to return in case of unsuccessful read of the properties file.
+ * @return the version from the Maven properties file on given path, embedded into JAR.
+ * @since 2.4.1
+ */
+ public static String readVersion( final ClassLoader cl, final String path, final String defaultVersion )
+ {
+ String version = defaultVersion;
+ InputStream is = null;
+ try
+ {
+ final Properties props = new Properties();
+ is = cl.getResourceAsStream( path );
+ if ( is != null )
+ {
+ props.load( is );
+ version = props.getProperty( "version" );
+ }
+ }
+ catch ( IOException e )
+ {
+ LOG.error( "Could not load/read version from " + path, e );
+ }
+ finally
+ {
+ Closeables.closeQuietly( is );
+ }
+ return version;
+ }
+
+ /**
+ * Shorthand method. Reads the version from a properties file using classloader that loaded up this class.
+ *
+ * @param path the binary path of the properties file to read from (might have more in case of shaded JAR).
+ * @param defaultVersion the version string to return in case of unsuccessful read of the properties file.
+ * @return the version from the Maven properties file on given path, embedded into JAR.
+ * @since 2.4.1
+ */
+ public static String readVersion( final String path, final String defaultVersion )
+ {
+ return readVersion( Version.class.getClassLoader(), path, defaultVersion );
+ }
+}
11 nexus-client-core/src/main/java/org/sonatype/nexus/client/rest/jersey/JerseyNexusClientFactory.java
View
@@ -14,6 +14,7 @@
import java.net.URI;
import java.util.Set;
+
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -24,7 +25,6 @@
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.params.ConnRoutePNames;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.CoreProtocolPNames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,9 +34,11 @@
import org.sonatype.nexus.client.internal.rest.NexusXStreamFactory;
import org.sonatype.nexus.client.internal.rest.XStreamXmlProvider;
import org.sonatype.nexus.client.internal.util.Template;
+import org.sonatype.nexus.client.internal.util.Version;
import org.sonatype.nexus.client.rest.ConnectionInfo;
import org.sonatype.nexus.client.rest.ProxyInfo;
import org.sonatype.nexus.client.rest.UsernamePasswordAuthenticationInfo;
+
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.filter.LoggingFilter;
import com.sun.jersey.api.json.JSONConfiguration;
@@ -110,7 +112,7 @@ protected ApacheHttpClient4 doCreateHttpClientFor( final ConnectionInfo connecti
// set UA
client.getClientHandler().getHttpClient().getParams().setParameter( CoreProtocolPNames.USER_AGENT,
- "Nexus-Client/1.0" );
+ "Nexus-Client/" + discoverClientVersion() );
// NXCM-4547 JERSEY-1293 Enforce proxy setting on httpclient
enforceProxyUri( config, client );
@@ -123,6 +125,11 @@ protected ApacheHttpClient4 doCreateHttpClientFor( final ConnectionInfo connecti
return client;
}
+ protected String discoverClientVersion()
+ {
+ return Version.readVersion( "META-INF/maven/org.sonatype.nexus.client/nexus-client-core/pom.properties", "unknown" );
+ }
+
// ==
/**
2  ...re/src/test/filtered-resources/META-INF/maven/org.sonatype.nexus.client/nexus-client-core/pom.properties
View
@@ -0,0 +1,2 @@
+# test
+version=${project.version}
64 ...-client-core/src/test/java/org/sonatype/nexus/client/rest/jersey/JerseyNexusTestClientUserAgentTest.java
View
@@ -0,0 +1,64 @@
+/*
+ * 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.client.rest.jersey;
+
+import java.net.MalformedURLException;
+
+import junit.framework.Assert;
+
+import org.apache.http.params.CoreProtocolPNames;
+import org.junit.Test;
+import org.sonatype.nexus.client.core.NexusClient;
+import org.sonatype.nexus.client.internal.util.Version;
+import org.sonatype.nexus.client.rest.BaseUrl;
+import org.sonatype.nexus.client.rest.NexusClientFactory;
+
+import com.sun.jersey.client.apache4.ApacheHttpClient4;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * UT for NexusClient UA, as it should carry the version from now on.
+ *
+ * @author cstamas
+ * @since 2.5
+ */
+public class JerseyNexusTestClientUserAgentTest
+ extends JerseyNexusClientTestSupport
+{
+ @Test
+ public void checkUAVersionIsProperlyReadAndSet()
+ throws MalformedURLException
+ {
+ final String version =
+ Version.readVersion( "META-INF/maven/org.sonatype.nexus.client/nexus-client-core/pom.properties", "foo" );
+ assertThat( "Version read must not return null!", version, notNullValue() );
+ assertThat( "Version read must not return the default (it should succeed in reading the stuff up)!", version,
+ not( equalTo( "foo" ) ) );
+
+ final NexusClientFactory factory = new JerseyNexusClientFactory();
+ final NexusClient client = factory.createFor( BaseUrl.baseUrlFrom( "https://repository.sonatype.org/" ) );
+ Assert.assertNotNull( client.getNexusStatus() );
+ final String userAgent =
+ (String) ( (ApacheHttpClient4) ( (JerseyNexusClient) client ).getClient() ).getClientHandler().getHttpClient().getParams().getParameter(
+ CoreProtocolPNames.USER_AGENT );
+ assertThat( "UA must not be null!", userAgent, notNullValue() );
+ assertThat( "UA must not be empty!", userAgent, containsString( "Nexus-Client" ) );
+ assertThat( "UA should be correct!", userAgent, equalTo( "Nexus-Client/" + version ) );
+ }
+}
57 nexus-core/src/main/java/org/sonatype/nexus/guice/AbstractInterceptorModule.java
View
@@ -0,0 +1,57 @@
+/*
+ * 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.guice;
+
+import java.lang.reflect.Method;
+
+import org.aopalliance.intercept.MethodInterceptor;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Key;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.name.Names;
+
+/**
+ * Workaround to automatically share method interceptors until
+ * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=403108">proper Sisu feature</a> is implemented.
+ *
+ * <p>
+ * This module is only bound once in its originating realm, when the bindInterceptor method is first called. The Nexus
+ * Plugin Manager can then see this module via the injected dynamic list of AbstractInterceptorModules and will install
+ * it in any plugins registered after this point.
+ *
+ * <p>
+ * Note: you can't contribute interceptors to earlier plugins or from a plugin to core, but the other direction works fine.
+ *
+ * @since 2.4
+ */
+public abstract class AbstractInterceptorModule
+ extends AbstractModule
+{
+ private boolean bound;
+
+ @Override
+ protected void bindInterceptor( final Matcher<? super Class<?>> classMatcher,
+ final Matcher<? super Method> methodMatcher,
+ final MethodInterceptor... interceptors )
+ {
+ if ( !bound )
+ {
+ // Explicitly bind module instance under a specific sub-type (not Module as Guice forbids that)
+ bind( Key.get( AbstractInterceptorModule.class, Names.named( getClass().getName() ) ) ).toInstance( this );
+ bound = true;
+ }
+ super.bindInterceptor( classMatcher, methodMatcher, interceptors );
+ }
+}
5 nexus-core/src/main/java/org/sonatype/nexus/guice/NexusModules.java
View
@@ -13,9 +13,9 @@
package org.sonatype.nexus.guice;
-import com.google.inject.AbstractModule;
import org.apache.shiro.guice.aop.ShiroAopModule;
-import org.sonatype.nexus.timing.TimingModule;
+
+import com.google.inject.AbstractModule;
/**
* Nexus guice modules.
@@ -33,7 +33,6 @@
@Override
protected void configure() {
install(new ShiroAopModule());
- install(new TimingModule());
}
}
17 nexus-core/src/main/java/org/sonatype/nexus/plugins/DefaultNexusPluginManager.java
View
@@ -40,6 +40,7 @@
import org.sonatype.guice.plexus.binders.PlexusXmlBeanModule;
import org.sonatype.guice.plexus.config.PlexusBeanModule;
import org.sonatype.inject.Parameters;
+import org.sonatype.nexus.guice.AbstractInterceptorModule;
import org.sonatype.nexus.guice.NexusModules.PluginModule;
import org.sonatype.nexus.mime.MimeSupport;
import org.sonatype.nexus.plugins.events.PluginActivatedEvent;
@@ -91,6 +92,8 @@
private final Map<String, String> variables;
+ private final List<AbstractInterceptorModule> interceptorModules;
+
private final Map<GAVCoordinate, PluginDescriptor> activePlugins = new HashMap<GAVCoordinate, PluginDescriptor>();
private final Map<GAVCoordinate, PluginResponse> pluginResponses = new HashMap<GAVCoordinate, PluginResponse>();
@@ -103,7 +106,8 @@ public DefaultNexusPluginManager( final RepositoryTypeRegistry repositoryTypeReg
final PluginRepositoryManager repositoryManager,
final DefaultPlexusContainer container,
final MimeSupport mimeSupport,
- final @Parameters Map<String, String> variables )
+ final @Parameters Map<String, String> variables,
+ final List<AbstractInterceptorModule> interceptorModules )
{
this.repositoryTypeRegistry = checkNotNull( repositoryTypeRegistry );
this.eventBus = checkNotNull( eventBus );
@@ -111,6 +115,7 @@ public DefaultNexusPluginManager( final RepositoryTypeRegistry repositoryTypeReg
this.container = checkNotNull( container );
this.mimeSupport = checkNotNull( mimeSupport );
this.variables = checkNotNull( variables );
+ this.interceptorModules = checkNotNull( interceptorModules );
}
// ----------------------------------------------------------------------
@@ -462,12 +467,12 @@ protected void configure()
final ClassSpace annSpace = new URLClassSpace( pluginRealm, scanList.toArray( new URL[scanList.size()] ) );
beanModules.add( new NexusAnnotatedBeanModule( annSpace, variables, exportedClassNames, repositoryTypes ) );
- final Module[] modules = {
- resourceModule,
- new PluginModule()
- };
+ final List<Module> modules = new ArrayList<Module>();
+ modules.add( resourceModule );
+ modules.add( new PluginModule() );
+ modules.addAll( interceptorModules );
- container.addPlexusInjector( beanModules, modules );
+ container.addPlexusInjector( beanModules, modules.toArray( new Module[modules.size()] ) );
for ( final RepositoryTypeDescriptor r : repositoryTypes )
{
8 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/AbstractChecksumContentValidator.java
View
@@ -40,6 +40,14 @@ public boolean isRemoteItemContentValid(final ProxyRepository proxy, final Reso
return true;
}
+ final ChecksumPolicy requestChecksumPolicy =
+ (ChecksumPolicy) req.getRequestContext().get( ChecksumPolicy.REQUEST_CHECKSUM_POLICY_KEY );
+ if ( requestChecksumPolicy != null )
+ {
+ // found, it overrides the repository-set checksum policy then
+ checksumPolicy = requestChecksumPolicy;
+ }
+
RemoteHashResponse remoteHash = retrieveRemoteHash( item, proxy, baseUrl );
// let compiler make sure I did not forget to populate validation results
2  nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumContentValidator.java
View
@@ -75,7 +75,7 @@
protected void cleanup( ProxyRepository proxy, RemoteHashResponse remoteHash, boolean contentValid )
throws LocalStorageException
{
- if ( !contentValid && remoteHash.getHashItem() != null )
+ if ( !contentValid && remoteHash != null && remoteHash.getHashItem() != null )
{
// TODO should we remove bad checksum if policy==WARN?
try
18 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumPolicy.java
View
@@ -12,6 +12,9 @@
*/
package org.sonatype.nexus.proxy.maven;
+import org.sonatype.nexus.proxy.RequestContext;
+import org.sonatype.nexus.proxy.ResourceStoreRequest;
+
/**
* Checksum policies known in Maven1/2 repositories where checksums are available according to maven layout.
*
@@ -39,6 +42,21 @@
*/
STRICT;
+ /**
+ * Key to be used in {@link ResourceStoreRequest} context to mark per-request "override" of the
+ * {@link MavenProxyRepository} checksum policy. The policy put with this key into request context will be applied
+ * to given request execution (and all sub-requests, as {@link RequestContext} hierarchy is used). The use of this
+ * key has effect only on Maven1/2 proxy repositories, as {@link ChecksumPolicy} itself is a Maven specific
+ * property, and is globally (on repository level) controlled by getters and setters on {@link MavenProxyRepository}
+ * interface. This "request override" - usually to relax the policy - should be used sparingly, for cases when it's
+ * known that global repository level checksum policy might pose a blocker to the tried content retrieval, but even
+ * a corrupted content would not pose any downstream problems (as Nexus would shield downstream consumers by some
+ * other means, like processing and checking content in-situ).
+ *
+ * @since 2.5
+ */
+ public static String REQUEST_CHECKSUM_POLICY_KEY = "request.maven.checksumPolicy";
+
public boolean shouldCheckChecksum()
{
return !IGNORE.equals( this );
7 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperation.java
View
@@ -12,8 +12,6 @@
*/
package org.sonatype.nexus.proxy.maven.metadata.operations;
-import static org.sonatype.nexus.proxy.maven.metadata.operations.MetadataUtil.isPluginEquals;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -23,6 +21,8 @@
import org.apache.maven.artifact.repository.metadata.Metadata;
import org.apache.maven.artifact.repository.metadata.Plugin;
+import static org.sonatype.nexus.proxy.maven.metadata.operations.MetadataUtil.isPluginPrefixAndArtifactIdEquals;
+
/**
* adds new plugin to metadata
*
@@ -88,8 +88,9 @@ public boolean perform( Metadata metadata )
{
if ( p.getArtifactId().equals( plugin.getArtifactId() ) )
{
- if ( isPluginEquals( p, plugin ) )
+ if ( isPluginPrefixAndArtifactIdEquals( p, plugin ) )
{
+ p.setName( plugin.getName() );
// plugin already enlisted
return false;
}
13 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/MetadataUtil.java
View
@@ -47,13 +47,14 @@ public static boolean isPluginEquals( Plugin p1, Plugin p2 )
p2.setName( "" );
}
- if ( StringUtils.equals( p1.getArtifactId(), p2.getArtifactId() )
- && StringUtils.equals( p1.getPrefix(), p2.getPrefix() ) && StringUtils.equals( p1.getName(), p2.getName() ) )
- {
- return true;
- }
+ return StringUtils.equals( p1.getArtifactId(), p2.getArtifactId() )
+ && StringUtils.equals( p1.getPrefix(), p2.getPrefix() ) && StringUtils.equals( p1.getName(), p2.getName() );
+ }
- return false;
+ public static boolean isPluginPrefixAndArtifactIdEquals( Plugin p1, Plugin p2 )
+ {
+ return StringUtils.equals( p1.getArtifactId(), p2.getArtifactId() )
+ && StringUtils.equals( p1.getPrefix(), p2.getPrefix() );
}
}
12 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/FilePrefixSource.java
View
@@ -303,6 +303,12 @@ protected void putFileItem( final ContentLocator content )
new DefaultStorageFileItem( getMavenRepository(), request, true, true, content );
try
{
+ // NXCM-5188: Remark to not get tempted to change these to storeItemWithChecksums() method:
+ // Since NEXUS-5418 was fixed (in 2.4), Nexus serves up ALL request for existing items that
+ // has extra trailing ".sha1" or ".md5" from item attributes. This means, that when prefix file
+ // is published in Nexus, there is no need anymore to save checksums to disk, as they will
+ // be served up just fine. This is true for all items in Nexus storage, not just prefix
+ // file related ones!
getMavenRepository().storeItem( true, file );
}
catch ( UnsupportedStorageOperationException e )
@@ -325,6 +331,12 @@ protected void deleteFileItem()
request.getRequestContext().put( Manager.ROUTING_INITIATED_FILE_OPERATION_FLAG_KEY, Boolean.TRUE );
try
{
+ // NXCM-5188: Remark to not get tempted to change these to deleteItemWithChecksums() method:
+ // Since NEXUS-5418 was fixed (in 2.4), Nexus serves up ALL request for existing items that
+ // has extra trailing ".sha1" or ".md5" from item attributes. This means, that when prefix file
+ // is published in Nexus, there is no need anymore to save checksums to disk, as they will
+ // be served up just fine. This is true for all items in Nexus storage, not just prefix
+ // file related ones!
getMavenRepository().deleteItem( true, request );
}
catch ( ItemNotFoundException e )
40 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategy.java
View
@@ -15,6 +15,8 @@
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
@@ -27,6 +29,7 @@
import org.sonatype.nexus.proxy.item.RepositoryItemUid;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.proxy.item.StorageItem;
+import org.sonatype.nexus.proxy.maven.ChecksumPolicy;
import org.sonatype.nexus.proxy.maven.MavenProxyRepository;
import org.sonatype.nexus.proxy.maven.routing.Config;
import org.sonatype.nexus.proxy.maven.routing.Manager;
@@ -47,6 +50,35 @@
extends AbstractRemoteStrategy
implements RemoteStrategy
{
+ private static final PrefixSource UNSUPPORTED_PREFIXSOURCE = new PrefixSource()
+ {
+ @Override
+ public boolean exists()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean supported()
+ {
+ return false;
+ }
+
+ @Override
+ public List<String> readEntries()
+ throws IOException
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public long getLostModifiedTimestamp()
+ {
+ return 0;
+ }
+
+ };
+
protected static final String ID = "prefix-file";
private final Config config;
@@ -86,14 +118,14 @@ public StrategyResult discover( final MavenProxyRepository mavenProxyRepository
Result unmarshalled = new TextFilePrefixSourceMarshaller( config ).read( item );
if ( !unmarshalled.supported() )
{
- return new StrategyResult( "Remote disabled automatic routing", null, false );
+ return new StrategyResult( "Remote disabled automatic routing", UNSUPPORTED_PREFIXSOURCE, false );
}
final PrefixSource prefixSource = new ArrayListPrefixSource( unmarshalled.entries() );
if ( prefixFileAgeInDays < 1 )
{
return new StrategyResult( "Remote publishes prefix file (is less than a day old), using it.",
- prefixSource, true );
+ prefixSource, true );
}
else
{
@@ -116,8 +148,10 @@ protected StorageFileItem retrieveFromRemoteIfExists( final MavenProxyRepository
throws IOException
{
final ResourceStoreRequest request = new ResourceStoreRequest( path );
- request.getRequestContext().put( Manager.ROUTING_INITIATED_FILE_OPERATION_FLAG_KEY, Boolean.TRUE );
request.setRequestRemoteOnly( true );
+ request.getRequestContext().put( Manager.ROUTING_INITIATED_FILE_OPERATION_FLAG_KEY, Boolean.TRUE );
+ // NXCM-5188: Disable checksum policy for prefix file request, it will be processed and checked anyway
+ request.getRequestContext().put( ChecksumPolicy.REQUEST_CHECKSUM_POLICY_KEY, ChecksumPolicy.IGNORE );
// check for remote presence, as fetching with setRequestRemoteOnly has a side effect of
// DELETING the file from local cache if not present remotely. In this case, prefix
20 nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSLocalRepositoryStorage.java
View
@@ -12,6 +12,8 @@
*/
package org.sonatype.nexus.proxy.storage.local.fs;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -22,9 +24,9 @@
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
-import org.codehaus.plexus.component.annotations.Component;
-import org.codehaus.plexus.util.StringUtils;
import org.sonatype.nexus.mime.MimeSupport;
import org.sonatype.nexus.proxy.ItemNotFoundException;
import org.sonatype.nexus.proxy.LocalStorageException;
@@ -45,16 +47,18 @@
import org.sonatype.nexus.proxy.repository.Repository;
import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException;
import org.sonatype.nexus.proxy.storage.local.AbstractLocalRepositoryStorage;
-import org.sonatype.nexus.proxy.storage.local.LocalRepositoryStorage;
import org.sonatype.nexus.proxy.wastebasket.Wastebasket;
import org.sonatype.nexus.util.ItemPathUtils;
+import com.google.common.base.Strings;
+
/**
* LocalRepositoryStorage that uses plain File System (relies on {@link File}) to implement it's functionality.
*
* @author cstamas
*/
-@Component( role = LocalRepositoryStorage.class, hint = DefaultFSLocalRepositoryStorage.PROVIDER_STRING )
+@Singleton
+@Named( DefaultFSLocalRepositoryStorage.PROVIDER_STRING )
public class DefaultFSLocalRepositoryStorage
extends AbstractLocalRepositoryStorage
{
@@ -63,11 +67,11 @@
private FSPeer fsPeer;
@Inject
- public DefaultFSLocalRepositoryStorage( Wastebasket wastebasket, LinkPersister linkPersister,
- MimeSupport mimeSupport, FSPeer fsPeer )
+ public DefaultFSLocalRepositoryStorage( final Wastebasket wastebasket, final LinkPersister linkPersister,
+ final MimeSupport mimeSupport, final FSPeer fsPeer )
{
super( wastebasket, linkPersister, mimeSupport );
- this.fsPeer = fsPeer;
+ this.fsPeer = checkNotNull( fsPeer );
}
protected FSPeer getFSPeer()
@@ -215,7 +219,7 @@ protected AbstractStorageItem retrieveItemFromFile( Repository repository, Resou
path = path.substring( 0, path.length() - 1 );
}
- if ( StringUtils.isEmpty( path ) )
+ if ( Strings.isNullOrEmpty( path ) )
{
path = RepositoryItemUid.PATH_ROOT;
}
5 nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSPeer.java
View
@@ -345,9 +345,8 @@ public boolean accept( File pathname )
}
else
{
- getLogger().warn( "Cannot list directory in repository {}, path \"{}\"",
- RepositoryStringUtils.getHumanizedNameString( repository ),
- target.getAbsolutePath() );
+ throw new LocalStorageException( "Cannot list directory in repository " + repository + ", path "
+ + target.getAbsolutePath() );
}
return result;
6 nexus-core/src/main/java/org/sonatype/nexus/scheduling/AbstractNexusRepositoriesTask.java
View
@@ -125,6 +125,9 @@ protected boolean hasIntersectingTasksThatRuns( Map<String, List<ScheduledTask<?
// get all activeTasks that runs and are descendants of AbstractNexusRepositoriesTask
for ( String taskType : activeTasks.keySet() )
{
+ // FIXME: Use nexus scheduler to perform any task lookups so the code can be isolated/managed in one place
+ // FIXME: avoid using plexus container apis directly
+
ComponentDescriptor<?> cd =
plexusContainer.getComponentDescriptor( SchedulerTask.class, SchedulerTask.class.getName(),
taskType );
@@ -163,7 +166,8 @@ protected boolean hasIntersectingTasksThatRuns( Map<String, List<ScheduledTask<?
}
else
{
- getLogger().warn( "Could not find component that implements SchedulerTask of type='" + taskType + "'!" );
+ // this can happen due to mismatch between plexus and sisu naming, nothing we need to WARN the user about however
+ getLogger().debug( "Could not find component that implements SchedulerTask of type='" + taskType + "'!" );
}
}
8 nexus-core/src/main/java/org/sonatype/nexus/timing/TimingModule.java
View
@@ -12,7 +12,10 @@
*/
package org.sonatype.nexus.timing;
-import com.google.inject.AbstractModule;
+import javax.inject.Named;
+
+import org.sonatype.nexus.guice.AbstractInterceptorModule;
+
import com.google.inject.matcher.Matchers;
/**
@@ -22,8 +25,9 @@
*
* @since 2.4
*/
+@Named
public class TimingModule
- extends AbstractModule
+ extends AbstractInterceptorModule
{
@Override
protected void configure() {
5 nexus-core/src/test/java/org/sonatype/nexus/plugins/DefaultNexusPluginManagerTest.java
View
@@ -18,12 +18,14 @@
import static org.mockito.Mockito.when;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.junit.Test;
import org.mockito.Mock;
+import org.sonatype.nexus.guice.AbstractInterceptorModule;
import org.sonatype.nexus.mime.MimeSupport;
import org.sonatype.nexus.plugins.repository.NexusPluginRepository;
import org.sonatype.nexus.plugins.repository.NoSuchPluginRepositoryArtifactException;
@@ -66,7 +68,8 @@ public void pluginDependenciesAreActivatedByGA()
{
final DefaultNexusPluginManager underTest = new DefaultNexusPluginManager(
repositoryTypeRegistry, eventBus, pluginRepositoryManager,
- new DefaultPlexusContainer(), mimeSupport, new HashMap<String, String>()
+ new DefaultPlexusContainer(), mimeSupport, new HashMap<String, String>(),
+ Collections.<AbstractInterceptorModule>emptyList()
)
{
@Override
104 nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperationTest.java
View
@@ -0,0 +1,104 @@
+/*
+ * 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.maven.metadata.operations;
+
+import java.util.Collections;
+
+import org.apache.maven.artifact.repository.metadata.Metadata;
+import org.apache.maven.artifact.repository.metadata.Plugin;
+import org.junit.Test;
+import org.sonatype.nexus.proxy.maven.metadata.operations.ModelVersionUtility.Version;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+
+public class AddPluginOperationTest
+{
+ @Test
+ public void testAddingPluginsDoesNotDuplicatesEntries()
+ throws Exception
+ {
+ final Metadata md = new Metadata();
+ {
+ final Plugin plugin = new Plugin();
+ plugin.setArtifactId( "bar-maven-plugin" );
+ plugin.setPrefix( "bar" );
+ plugin.setName( "Bar plugin" );
+ md.addPlugin( plugin );
+ }
+
+ // new plugin addition
+ {
+ final Plugin plugin1 = new Plugin();
+ plugin1.setPrefix( "foo" );
+ plugin1.setArtifactId( "foo-maven-plugin" );
+ plugin1.setName( "Foo plugin" );
+ MetadataBuilder.changeMetadata( md, Collections.<MetadataOperation> singletonList( new AddPluginOperation(
+ new PluginOperand( Version.V110, plugin1 ) ) ) );
+ }
+ assertThat( "New plugin should be added!", md.getPlugins(), hasSize( 2 ) );
+ assertThat( md.getPlugins().get( 0 ).getPrefix(), equalTo( "bar" ) );
+ assertThat( md.getPlugins().get( 0 ).getName(), equalTo( "Bar plugin" ) );
+ assertThat( md.getPlugins().get( 1 ).getPrefix(), equalTo( "foo" ) );
+ assertThat( md.getPlugins().get( 1 ).getName(), equalTo( "Foo plugin" ) );
+
+ // existing plugin addition
+ {
+ final Plugin plugin1 = new Plugin();
+ plugin1.setPrefix( "foo" );
+ plugin1.setArtifactId( "foo-maven-plugin" );
+ plugin1.setName( "The new Foo plugin" );
+ MetadataBuilder.changeMetadata( md, Collections.<MetadataOperation> singletonList( new AddPluginOperation(
+ new PluginOperand( Version.V110, plugin1 ) ) ) );
+ }
+ assertThat( "No new plugin should be added!", md.getPlugins(), hasSize( 2 ) );
+ assertThat( md.getPlugins().get( 0 ).getPrefix(), equalTo( "bar" ) );
+ assertThat( md.getPlugins().get( 0 ).getName(), equalTo( "Bar plugin" ) );
+ assertThat( md.getPlugins().get( 1 ).getPrefix(), equalTo( "foo" ) );
+ assertThat( md.getPlugins().get( 1 ).getName(), equalTo( "The new Foo plugin" ) );
+
+ // existing plugin addition
+ {
+ final Plugin plugin1 = new Plugin();
+ plugin1.setPrefix( "bar" );
+ plugin1.setArtifactId( "bar-maven-plugin" );
+ plugin1.setName( "The new Bar plugin" );
+ MetadataBuilder.changeMetadata( md, Collections.<MetadataOperation> singletonList( new AddPluginOperation(
+ new PluginOperand( Version.V110, plugin1 ) ) ) );
+ }
+ assertThat( "No new plugin should be added!", md.getPlugins(), hasSize( 2 ) );
+ assertThat( md.getPlugins().get( 0 ).getPrefix(), equalTo( "bar" ) );
+ assertThat( md.getPlugins().get( 0 ).getName(), equalTo( "The new Bar plugin" ) );
+ assertThat( md.getPlugins().get( 1 ).getPrefix(), equalTo( "foo" ) );
+ assertThat( md.getPlugins().get( 1 ).getName(), equalTo( "The new Foo plugin" ) );
+
+ // new plugin addition wrt plugin order, plugins are ordered by ArtifactID
+ {
+ final Plugin plugin1 = new Plugin();
+ plugin1.setPrefix( "alpha" );
+ plugin1.setArtifactId( "alpha-maven-plugin" );
+ plugin1.setName( "Alpha plugin" );
+ MetadataBuilder.changeMetadata( md, Collections.<MetadataOperation> singletonList( new AddPluginOperation(
+ new PluginOperand( Version.V110, plugin1 ) ) ) );
+ }
+ assertThat( "New plugin should be added!", md.getPlugins(), hasSize( 3 ) );
+ assertThat( md.getPlugins().get( 0 ).getPrefix(), equalTo( "alpha" ) );
+ assertThat( md.getPlugins().get( 0 ).getName(), equalTo( "Alpha plugin" ) );
+ assertThat( md.getPlugins().get( 1 ).getPrefix(), equalTo( "bar" ) );
+ assertThat( md.getPlugins().get( 1 ).getName(), equalTo( "The new Bar plugin" ) );
+ assertThat( md.getPlugins().get( 2 ).getPrefix(), equalTo( "foo" ) );
+ assertThat( md.getPlugins().get( 2 ).getName(), equalTo( "The new Foo plugin" ) );
+ }
+}
44 nexus-core/src/test/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategyTest.java
View
@@ -275,4 +275,48 @@ public boolean execute( HttpServletRequest request, HttpServletResponse response
}
}
+ /**
+ * https://issues.sonatype.org/browse/NXCM-5188 Strict Checksum enforcement breaks Automatic Routing
+ * <p>
+ * Prefix file retrieval uses plain Proxy transport to get the file from remote peer. This causes problems (actually
+ * prevents happening it) if following conditions are met: Proxy repository has STRICT checksum policy and remote
+ * prefix file has no checksum published. In this case, relaxing the policy for this request only is okay to do,
+ * since "this" Nexus will properly process the prefix file anyway, detecting any problems with it, thus protecting
+ * downstream clients too.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void discoverPlaintextPrefixFileWithoutChecksumsWithStrictChecksumPolicy()
+ throws Exception
+ {
+ server.stop();
+ server =
+ Server.withPort( remoteServerPort ).serve( "/.meta/prefixes.txt" ).withBehaviours(
+ Behaviours.content( prefixFile1( true ) ) ).start();
+ try
+ {
+ // setting the policy to STRICT, and note that server set up above publishes
+ // the prefix file only, no checksums!
+ final MavenProxyRepository mavenProxyRepository =
+ getRepositoryRegistry().getRepositoryWithFacet( PROXY_REPO_ID, MavenProxyRepository.class );
+ mavenProxyRepository.setChecksumPolicy( ChecksumPolicy.STRICT );
+ getApplicationConfiguration().saveConfiguration();
+
+ final RemoteStrategy subject = lookup( RemoteStrategy.class, RemotePrefixFileStrategy.ID );
+ final StrategyResult result = subject.discover( mavenProxyRepository );
+ assertThat( result.getMessage(),
+ equalTo( "Remote publishes prefix file (is less than a day old), using it." ) );
+
+ final PrefixSource entrySource = result.getPrefixSource();
+ assertThat( entrySource.supported(), is( true ) );
+ assertThat( entrySource.readEntries(), contains( "/org/apache/maven", "/org/sonatype", "/eu/flatwhite" ) );
+ assertThat( entrySource.readEntries().size(), equalTo( 3 ) );
+ }
+ finally
+ {
+ server.stop();
+ }
+ }
+
}
216 ...rc/test/java/org/sonatype/nexus/proxy/storage/local/fs/NEXUS5612DefaultFSLocalRepositoryStorageTest.java
View
@@ -0,0 +1,216 @@
+/*
+ * 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.local.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.junit.Test;
+import org.sonatype.configuration.ConfigurationException;
+import org.sonatype.nexus.configuration.model.CLocalStorage;
+import org.sonatype.nexus.configuration.model.CRepository;
+import org.sonatype.nexus.configuration.model.DefaultCRepository;
+import org.sonatype.nexus.mime.MimeSupport;
+import org.sonatype.nexus.proxy.AbstractProxyTestEnvironment;
+import org.sonatype.nexus.proxy.EnvironmentBuilder;
+import org.sonatype.nexus.proxy.ItemNotFoundException;
+import org.sonatype.nexus.proxy.LocalStorageException;
+import org.sonatype.nexus.proxy.ResourceStoreRequest;
+import org.sonatype.nexus.proxy.item.LinkPersister;
+import org.sonatype.nexus.proxy.item.StorageCollectionItem;
+import org.sonatype.nexus.proxy.item.StorageFileItem;
+import org.sonatype.nexus.proxy.maven.MavenHostedRepository;
+import org.sonatype.nexus.proxy.maven.RepositoryPolicy;
+import org.sonatype.nexus.proxy.maven.maven2.M2Repository;
+import org.sonatype.nexus.proxy.maven.maven2.M2RepositoryConfiguration;
+import org.sonatype.nexus.proxy.repository.Repository;
+import org.sonatype.nexus.proxy.walker.AbstractFileWalkerProcessor;
+import org.sonatype.nexus.proxy.walker.DefaultWalkerContext;
+import org.sonatype.nexus.proxy.walker.Walker;
+import org.sonatype.nexus.proxy.walker.WalkerContext;
+import org.sonatype.nexus.proxy.walker.WalkerException;
+import org.sonatype.nexus.proxy.wastebasket.Wastebasket;
+
+import com.google.common.primitives.Ints;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.Matchers.not;
+
+/**
+ * Tests {@link DefaultFSLocalRepositoryStorage} in relation to issue https://issues.sonatype.org/browse/NEXUS-5612
+ */
+public class NEXUS5612DefaultFSLocalRepositoryStorageTest
+ extends AbstractProxyTestEnvironment
+{
+ private static final String MINE_MESSAGE = "You stepped a mine!";
+
+ private final String REPO_ID = "testrepo";
+
+ private File localStorageDirectory;
+
+ @Override
+ protected EnvironmentBuilder getEnvironmentBuilder()
+ throws Exception
+ {
+ // we need one hosted repo only, so build it
+ return new EnvironmentBuilder()
+ {
+ @Override
+ public void startService()
+ {
+ }
+
+ @Override
+ public void stopService()
+ {
+ }
+
+ @Override
+ public void buildEnvironment( AbstractProxyTestEnvironment env )
+ throws ConfigurationException, IOException, ComponentLookupException
+ {
+ final PlexusContainer container = env.getPlexusContainer();
+ localStorageDirectory =
+ env.getApplicationConfiguration().getWorkingDirectory( "proxy/store/" + REPO_ID );
+
+ // ading one hosted only
+ final M2Repository repo = (M2Repository) container.lookup( Repository.class, "maven2" );
+ CRepository repoConf = new DefaultCRepository();
+ repoConf.setProviderRole( Repository.class.getName() );
+ repoConf.setProviderHint( "maven2" );
+ repoConf.setId( REPO_ID );
+ repoConf.setName( REPO_ID );
+ repoConf.setLocalStorage( new CLocalStorage() );
+ repoConf.getLocalStorage().setProvider( "file" );
+ repoConf.getLocalStorage().setUrl( localStorageDirectory.toURI().toURL().toString() );
+ Xpp3Dom exRepo = new Xpp3Dom( "externalConfiguration" );
+ repoConf.setExternalConfiguration( exRepo );
+ M2RepositoryConfiguration exRepoConf = new M2RepositoryConfiguration( exRepo );
+ exRepoConf.setRepositoryPolicy( RepositoryPolicy.RELEASE );
+ repo.configure( repoConf );
+ env.getApplicationConfiguration().getConfigurationModel().addRepository( repoConf );
+ env.getRepositoryRegistry().addRepository( repo );
+ }
+ };
+ }
+
+ private static class TestingDefaultFSPeer
+ extends DefaultFSPeer
+ {
+ private final List<String> minedPaths;
+
+ public TestingDefaultFSPeer( List<String> minedPaths )
+ {
+ this.minedPaths = minedPaths;
+ }
+
+ @Override
+ public Collection<File> listItems( final Repository repository, final File repositoryBaseDir,
+ final ResourceStoreRequest request, final File target )
+ throws ItemNotFoundException, LocalStorageException
+ {
+ if ( minedPaths.contains( request.getRequestPath() ) )
+ {
+ throw new LocalStorageException( MINE_MESSAGE );
+ }
+ return super.listItems( repository, repositoryBaseDir, request, target );
+ }
+ }
+
+ /**
+ * Tests interaction when listing a directory and FSPeer throws LocalStorageException due to {@link File#list()}
+ * returning null.
+ */
+ @Test
+ public void testListFilesThrowsLocalStorageException()
+ throws Exception
+ {
+ // prepare a repo to walk, copy some stuff under local storage
+ FileUtils.copyDirectoryStructure( getTestFile( "target/test-classes/repo1" ), localStorageDirectory );
+ // count the existing files
+ final int filesInRepo =
+ Ints.checkedCast( org.sonatype.nexus.util.FileUtils.fileSizesInDirectory( localStorageDirectory ) );
+
+ // list of parents that will crack
+ final List<String> minedParents = Collections.singletonList( "/activemq/activemq-core/1.2" );
+ // list of visited children during walk
+ final List<String> visitedChildren = new ArrayList<String>();
+ // list of parent collections having onCollectionExit invoked with
+ final List<String> exitedCollections = new ArrayList<String>();
+
+ // get the repo and tap in modified LS implementation
+ final MavenHostedRepository testRepo =
+ getRepositoryRegistry().getRepositoryWithFacet( REPO_ID, MavenHostedRepository.class );
+ // tap in FSPeer that has mines planted
+ // everything as usual, exception FSPeer implementation
+ testRepo.setLocalStorage( new DefaultFSLocalRepositoryStorage( lookup( Wastebasket.class ),
+ lookup( LinkPersister.class ), lookup( MimeSupport.class ), new TestingDefaultFSPeer( minedParents ) ) );
+
+ // Create context and processors for the walk
+ final DefaultWalkerContext context = new DefaultWalkerContext( testRepo, new ResourceStoreRequest( "/" ) );
+ context.getProcessors().add( new AbstractFileWalkerProcessor()
+ {
+ @Override
+ protected void processFileItem( WalkerContext context, StorageFileItem fItem )
+ throws Exception
+ {
+ visitedChildren.add( fItem.getPath() );
+ }
+
+ @Override
+ public void onCollectionExit( WalkerContext context, StorageCollectionItem coll )
+ throws Exception
+ {
+ exitedCollections.add( coll.getPath() );
+ }
+
+ } );
+ final Walker walker = lookup( Walker.class );
+
+ // walk
+ try
+ {
+ walker.walk( context );
+ assertThat( "Walk must fail as we planted a mine in there!", false );
+ }
+ catch ( WalkerException e )
+ {
+ assertThat( "Reason must state walk is \"aborted\"", e.getMessage().toLowerCase().contains( "aborted" ) );
+ assertThat( "Reason must refer to our repo ID=" + REPO_ID,
+ e.getMessage().toLowerCase().contains( REPO_ID.toLowerCase() ) );
+ assertThat( "Cause must be LocalStorageException", e.getCause() instanceof LocalStorageException );
+ assertThat( "Cause message must be the one we defined", e.getCause().getMessage(), equalTo( MINE_MESSAGE ) );
+ assertThat( "Context must be marked as stopped", context.isStopped() );
+ assertThat( "Context stop-cause must be same as WalkerException's cause",
+ context.getStopCause() == e.getCause() );
+ assertThat( "Walk must stop before visiting all files", visitedChildren.size(), lessThan( filesInRepo ) );
+ for ( String minedParent : minedParents )
+ {
+ assertThat( "WalkerProcessor#onCollectionExit must not be invoked with parent being mined",
+ exitedCollections, not( contains( minedParent ) ) );
+ }
+ }
+ }
+}
290 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/AbstractMirrorPanel.js
View
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+/*global NX,Sonatype,Ext,Nexus*/
+NX.define('Sonatype.repoServer.AbstractMirrorPanel', {
+ extend : 'Nexus.ext.FormPanel',
+
+ require : ['Sonatype.repoServer.referenceData'],
+
+ requirejs : ['Nexus/config'],
+
+ MIRROR_URL_REGEXP : /^(?:http|https):\/\//i,
+
+ constructor : function(config) {
+ this.mirrorStatusTask = {
+ run : function() {
+ Ext.Ajax.request({
+ url : Sonatype.config.repos.urls.repoMirrorStatus + '/' + this.payload.data.id,
+ callback : this.statusCallback,
+ scope : this
+ });
+ },
+ interval : 5000, // poll every 5 seconds
+ scope : this
+ };
+
+ this.mirrorRecordConstructor = Ext.data.Record.create([
+ {
+ name : 'id'
+ },
+ {
+ name : 'url',
+ sortType : Ext.data.SortTypes.asUCString
+ }
+ ]);
+
+ this.mirrorReader = new Ext.data.JsonReader({
+ root : 'data',
+ id : 'id'
+ }, this.mirrorRecordConstructor);
+
+ this.mirrorDataStore = new Ext.data.Store({
+ url : Sonatype.config.repos.urls.repoMirrors + '/' + this.payload.data.id,
+ reader : this.mirrorReader,
+ sortInfo : {
+ field : 'url',
+ direction : 'ASC'
+ },
+ autoLoad : false
+ });
+
+ var defaultConfig = {
+ uri : Sonatype.config.repos.urls.repoMirrors + '/' + this.payload.data.id,
+ referenceData : Sonatype.repoServer.referenceData.repoMirrors,
+ dataStores : [this.mirrorDataStore],
+ dataModifiers : {
+ load : {
+ 'rootData' : this.loadMirrors.createDelegate(this)
+ },
+ submit : {
+ 'rootData' : this.saveMirrors.createDelegate(this)
+ }
+ },
+ listeners : {
+ submit : {
+ fn : this.submitHandler,
+ scope : this
+ },
+ activate : {
+ fn : this.activateHandler,
+ scope : this
+ },
+ deactivate : {
+ fn : this.deactivateHandler,
+ scope : this
+ },
+ destroy : {
+ fn : this.destroyHandler,
+ scope : this
+ }
+ }
+ };
+
+ Ext.apply(this, config || {}, defaultConfig);
+
+ Sonatype.repoServer.AbstractMirrorPanel.superclass.constructor.call(this, {});
+ },
+
+ addNewMirrorUrl : function() {
+ var
+ i,
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ nodes = treePanel.root.childNodes,
+ urlField = this.find('name', 'mirrorUrl')[0],
+ url = urlField.getRawValue();
+
+ if (urlField.isValid() && url) {
+ for (i = 0; i < nodes.length; i += 1) {
+ if (url === nodes[i].attributes.payload.url) {
+ urlField.markInvalid('This URL already exists');
+ return;
+ }
+ }
+
+ urlField.clearInvalid();
+
+ this.addUrlNode(treePanel, url, url, Sonatype.config.extPath + '/resources/images/default/tree/leaf.gif');
+ urlField.setRawValue('');
+ urlField.setValue('');
+ }
+ },
+
+ addUrlNode : function(treePanel, url, id, icon) {
+ var validId, manualUrl;
+ if (url === id) {
+ validId = Ext.id();
+ manualUrl = true;
+ }
+ else {
+ validId = id;
+ manualUrl = false;
+ }
+ treePanel.root.appendChild(new Ext.tree.TreeNode({
+ id : id,
+ text : url,
+ href : url,
+ hrefTarget : '_new',
+ payload : {
+ id : manualUrl ? '' : id,
+ url : url
+ },
+ allowChildren : false,
+ draggable : true,
+ leaf : true,
+ icon : icon
+ }));
+ },
+
+ removeMirrorUrl : function() {
+ var
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ selectedNode = treePanel.getSelectionModel().getSelectedNode();
+
+ if (selectedNode) {
+ treePanel.root.removeChild(selectedNode);
+ }
+ },
+
+ removeAllMirrorUrls : function() {
+ var
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ treeRoot = treePanel.root;
+
+ while (treeRoot.lastChild) {
+ treeRoot.removeChild(treeRoot.lastChild);
+ }
+ },
+
+ loadMirrors : function(arr, srcObj, fpanel) {
+ var
+ i, j, childNodes, found,
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ mirrorArray = [];
+
+ for (i = 0; i < arr.length; i += 1) {
+ childNodes = treePanel.getRootNode().childNodes;
+ found = false;
+ if (childNodes && childNodes.length) {
+ for (j = 0; j < childNodes.length; j += 1) {
+ if (arr[i].id === childNodes[j].id) {
+ mirrorArray[i] = {
+ id : arr[i].id,
+ url : arr[i].url,
+ icon : childNodes[j].ui.iconNode.src
+ };
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ mirrorArray[i] = {
+ id : arr[i].id,
+ url : arr[i].url,
+ icon : Sonatype.config.extPath + '/resources/images/default/tree/leaf.gif'
+ };
+ }
+ }
+
+ this.removeAllMirrorUrls();
+
+ for (i = 0; i < arr.length; i += 1) {
+ this.addUrlNode(treePanel, mirrorArray[i].url, mirrorArray[i].id, mirrorArray[i].icon);
+ }
+
+ return arr;
+ },
+
+ saveMirrors : function(val, fpanel) {
+ var
+ i,
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ outputArr = [],
+ nodes = treePanel.root.childNodes;
+
+ for (i = 0; i < nodes.length; i += 1) {
+ outputArr[i] = nodes[i].attributes.payload;
+ }
+
+ return outputArr;
+ },
+
+ getActionURL : function() {
+ return this.uri;
+ },
+
+ getSaveMethod : function() {
+ return 'POST';
+ },
+
+ statusCallback : function(options, success, response) {
+ if (success) {
+ var
+ i, j, data, item,
+ statusResp = Ext.decode(response.responseText),
+ treePanel = this.find('name', 'mirror-url-list')[0],
+ childNodes = treePanel.getRootNode().childNodes;
+
+ if (statusResp.data) {
+ data = statusResp.data;
+ if (data && data.length) {
+ for (i = 0; i < data.length; i += 1) {
+ item = data[i];
+ if (childNodes && childNodes.length) {
+ for (j = 0; j < childNodes.length; j += 1) {
+ if (item.id === childNodes[j].id) {
+ childNodes[j].getUI().getIconEl().src = item.status === 'Blacklisted' ? (Sonatype.config.extPath
+ + '/resources/images/default/tree/drop-no.gif') : (Sonatype.config.extPath
+ + '/resources/images/default/tree/drop-yes.gif');
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ // don't add icons, no need to bother the user with this
+ Ext.TaskMgr.stop(this.mirrorStatusTask);
+ }
+ },
+
+ submitHandler : function(form, action, receivedData) {
+ this.loadMirrors(receivedData, null, this);
+ },
+
+ activateHandler : function(panel) {
+ // HACK: this.predefinedMirrorDataStore is only defined in ProxyMirrorEditor class, only attempt to call load() if the field exists
+ // HACK: Fix for NEXUS-5060, probably better way to fix however.
+ if (this.predefinedMirrorDataStore) {
+ this.predefinedMirrorDataStore.load();
+ }
+
+ if (panel.payload.data.repoType === 'proxy') {
+ Ext.TaskMgr.start(this.mirrorStatusTask);
+ }
+ },
+
+ deactivateHandler : function(panel) {
+ if (panel.payload.data.repoType === 'proxy') {
+ Ext.TaskMgr.stop(this.mirrorStatusTask);
+ }
+ },
+
+ destroyHandler : function(component) {
+ if (component.payload.data.repoType === 'proxy') {
+ Ext.TaskMgr.stop(this.mirrorStatusTask);
+ }
+ }
+});
144 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/HostedMirrorEditor.js
View
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+/*global NX,Sonatype,Ext,Nexus*/
+NX.define('Sonatype.repoServer.HostedMirrorEditor', {
+ extend : 'Sonatype.repoServer.AbstractMirrorPanel',
+ requirejs : ['Sonatype/all'],
+
+ constructor : function(config) {
+ Ext.apply(this, config || {});
+ var ht = Sonatype.repoServer.resources.help.repoMirrors,
+ self = this;
+
+ Sonatype.repoServer.HostedMirrorEditor.superclass.constructor.call(this, {
+ items : [
+ {
+ xtype : 'panel',
+ style : 'padding-top: 20px',
+ layout : 'column',
+ items : [
+ {
+ xtype : 'panel',
+ layout : 'form',
+ labelWidth : 150,
+ width : 430,
+ items : [
+ {
+ xtype : 'textfield',
+ fieldLabel : 'Mirror URL',
+ helpText : ht.mirrorUrl,
+ name : 'mirrorUrl',
+ width : 255,
+ emptyText : 'Enter URL...',
+ selectOnFocus : true,
+ allowBlank : true,
+ validator : function(v) {
+ if (v === '' || v.match(self.MIRROR_URL_REGEXP)) {
+ return true;
+ }
+
+ return 'Protocol must be http:// or https://';
+ }
+ }
+ ]
+ },
+ {
+ xtype : 'panel',
+ width : 120,
+ items : [
+ {
+ xtype : 'button',
+ text : 'Add',
+ style : 'padding-left: 7px',
+ minWidth : 100,
+ id : 'button-add',
+ handler : this.addNewMirrorUrl,
+ scope : this
+ }
+ ]
+ }
+ ]
+ },
+ {
+ xtype : 'panel',
+ layout : 'column',
+ autoHeight : true,
+ style : 'padding-left: 155px',
+ items : [
+ {
+ xtype : 'treepanel',
+ name : 'mirror-url-list',
+ title : 'Mirror URLs',
+ border : true,
+ bodyBorder : true,
+ bodyStyle : 'background-color:#FFFFFF; border: 1px solid #B5B8C8',
+ style : 'padding: 0 20px 0 0',
+ width : 275,
+ height : 300,
+ animate : true,
+ lines : false,
+ autoScroll : true,
+ containerScroll : true,
+ rootVisible : false,
+ enableDD : false,
+ root : new Ext.tree.TreeNode({
+ text : 'root'
+ })
+ },
+ {
+ xtype : 'panel',
+ width : 120,
+ items : [
+ {
+ xtype : 'button',
+ text : 'Remove',
+ style : 'padding-left: 6px',
+ minWidth : 100,
+ id : 'button-remove',
+ handler : this.removeMirrorUrl,
+ scope : this
+ },
+ {
+ xtype : 'button',
+ text : 'Remove All',
+ style : 'padding-left: 6px; margin-top: 5px',
+ minWidth : 100,
+ id : 'button-remove-all',
+ handler : this.removeAllMirrorUrls,
+ scope : this
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ });
+ }
+}, function() {
+ Sonatype.Events.addListener('repositoryViewInit', function(cardPanel, rec) {
+ var sp = Sonatype.lib.Permissions;
+ if (rec.data.resourceURI && sp.checkPermission('nexus:repositorymirrors', sp.READ) && rec.data.userManaged)
+ {
+ if (rec.data.repoType === 'hosted')
+ {
+ cardPanel.add(new Sonatype.repoServer.HostedMirrorEditor({
+ payload : rec,
+ tabTitle : 'Mirrors',
+ name : 'mirrors'
+ }));
+ }
+ }
+ });
+});
+
+
172 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/Maven2InformationPanel.js
View
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+/*global NX, Sonatype, Ext*/
+NX.define('Sonatype.repoServer.Maven2InformationPanel', {
+ extend : 'Ext.form.FormPanel',
+ requirejs : ['Sonatype/init'],
+
+ constructor : function(config) {
+ Ext.apply(this, config || {}, {
+ halfSize : false
+ });
+
+ this.sp = Sonatype.lib.Permissions;
+
+ this.linkDivId = Ext.id();
+ this.linkLabelId = Ext.id();
+
+ Sonatype.repoServer.Maven2InformationPanel.superclass.constructor.call(this, {
+ title : 'Maven',
+ autoScroll : true,
+ border : true,
+ frame : true,
+ collapsible : false,
+ collapsed : false,
+ items : [
+ {
+ xtype : 'displayfield',
+ fieldLabel : 'Group',
+ name : 'groupId',
+ anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
+ allowBlank : true,
+ readOnly : true
+ },
+ {
+ xtype : 'displayfield',
+ fieldLabel : 'Artifact',
+ name : 'artifactId',
+ anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
+ allowBlank : true,
+ readOnly : true
+ },
+ {
+ xtype : 'displayfield',
+ fieldLabel : 'Version',
+ name : 'baseVersion',
+ anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
+ allowBlank : true,
+ readOnly : true
+ },
+ {
+ xtype : 'displayfield',
+ fieldLabel : 'Classifier',
+ name : 'classifier',
+ anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
+ allowBlank : true,
+ readOnly : true
+ },
+ {
+ xtype : 'displayfield',
+ fieldLabel : 'Extension',
+ name : 'extension',
+ anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
+ allowBlank : true,
+ readOnly : true
+ },
+ {
+ xtype : 'textarea',
+ fieldLabel : 'XML',
+ anchor : Sonatype.view.FIELD_OFFSET,
+ height : 120,
+ name : 'dependencyXmlChunk',
+ allowBlank : true,
+ readOnly : true
+ }
+ ]
+ });
+ },
+
+ showArtifact : function(data, artifactContainer) {
+ this.data = data;
+ if (data) {
+ Ext.Ajax.request({
+ url : this.data.resourceURI + '?describe=maven2&isLocal=true',
+ callback : function(options, isSuccess, response) {
+ if (isSuccess) {
+ var infoResp = Ext.decode(response.responseText);
+
+ // hide classifier if empty
+ if (this.data.classifier) {
+ this.find('name', 'classifier')[0].show();
+ }
+ else {
+ this.find('name', 'classifier')[0].hide();
+ }
+ this.form.setValues(infoResp.data);
+ artifactContainer.showTab(this);
+ }
+ else {
+ if (response.status === 404) {
+ artifactContainer.hideTab(this);
+ }
+ else {
+ Sonatype.utils.connectionError(response, 'Unable to retrieve Maven information.');
+ }
+ }
+ },
+ scope : this,
+ method : 'GET',
+ suppressStatus : '404'
+ });
+ }
+ else {
+ this.find('name', 'groupId')[0].setRawValue(null);
+ this.find('name', 'artifactId')[0].setRawValue(null);
+ this.find('name', 'baseVersion')[0].setRawValue(null);
+ this.find('name', 'classifier')[0].setRawValue(null);
+ this.find('name', 'extension')[0].setRawValue(null);
+ this.find('name', 'dependencyXmlChunk')[0].setRawValue(null);
+ }
+ }
+}, function() {
+
+ Sonatype.Events.addListener('fileContainerInit', function(items) {
+ items.push(new Sonatype.repoServer.Maven2InformationPanel({
+ name : 'maven2InformationPanel',
+ tabTitle : 'Maven',
+ preferredIndex : 10
+ }));
+ });
+
+ Sonatype.Events.addListener('fileContainerUpdate', function(artifactContainer, data) {
+ var panel = artifactContainer.find('name', 'maven2InformationPanel')[0];
+
+ if (data && data.leaf) {
+ panel.showArtifact(data, artifactContainer);
+ }
+ else {
+ panel.showArtifact(null, artifactContainer);
+ }
+ });
+
+ Sonatype.Events.addListener('artifactContainerInit', function(items) {
+ items.push(new Sonatype.repoServer.Maven2InformationPanel({
+ name : 'maven2InformationPanel',
+ tabTitle : 'Maven',
+ preferredIndex : 10
+ }));
+ });
+
+ Sonatype.Events.addListener('artifactContainerUpdate', function(artifactContainer, payload) {
+ var panel = artifactContainer.find('name', 'maven2InformationPanel')[0];
+
+ if (payload && payload.leaf) {
+ panel.showArtifact(payload, artifactContainer);
+ }
+ else {
+ panel.showArtifact(null, artifactContainer);
+ }
+
+ });
+
+});
178 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/ProxyMirrorEditor.js
View
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+/*global NX,Sonatype,Ext,Nexus*/
+NX.define('Sonatype.repoServer.ProxyMirrorEditor', {
+ extend : 'Sonatype.repoServer.AbstractMirrorPanel',
+ requirejs : ['Sonatype/all'],
+
+ constructor : function(config) {
+ Ext.apply(this, config || {}, {});
+ var ht = Sonatype.repoServer.resources.help.repoMirrors,
+ self = this;
+
+ this.mirrorRecordConstructor = Ext.data.Record.create([
+ {
+ name : 'id'
+ },
+ {
+ name : 'url',
+ sortType : Ext.data.SortTypes.asUCString
+ }
+ ]);
+
+ this.mirrorReader = new Ext.data.JsonReader({
+ root : 'data',
+ id : 'id'
+ }, this.mirrorRecordConstructor);
+
+ this.predefinedMirrorDataStore = new Ext.data.Store({
+ url : Sonatype.config.repos.urls.repoPredefinedMirrors + '/' + this.payload.data.id,
+ reader : this.mirrorReader,
+ sortInfo : {
+ field : 'url',
+ direction : 'ASC'
+ }
+ });
+
+ Sonatype.repoServer.ProxyMirrorEditor.superclass.constructor.call(this, {
+ items : [
+ {
+ xtype : 'panel',
+ style : 'padding-top: 20px',
+ layout : 'column',
+ items : [
+ {
+ xtype : 'panel',
+ layout : 'form',
+ labelWidth : 150,
+ width : 430,
+ items : [
+ {
+ xtype : 'combo',
+ fieldLabel : 'Mirror URL',
+ helpText : ht.mirrorUrl,
+ name : 'mirrorUrl',
+ width : 238,
+ listWidth : 238,
+ store : this.predefinedMirrorDataStore,
+ displayField : 'url',
+ valueField : 'id',
+ editable : true,
+ forceSelection : false,
+ mode : 'local',
+ triggerAction : 'all',
+ emptyText : 'Enter or Select URL...',
+ selectOnFocus : true,
+ allowBlank : true,
+ validator : function(v) {
+ if (v === '' || v.match(self.MIRROR_URL_REGEXP)) {
+ return true;
+ }
+
+ return 'Protocol must be http:// or https://';
+ }
+ }
+ ]
+ },
+ {
+ xtype : 'panel',
+ width : 120,
+ items : [
+ {
+ xtype : 'button',
+ text : 'Add',
+ style : 'padding-left: 7px',
+ minWidth : 100,
+ id : 'button-add',
+ handler : this.addNewMirrorUrl,
+ scope : this
+ }
+ ]
+ }
+ ]
+ },
+ {
+ xtype : 'panel',
+ layout : 'column',
+ autoHeight : true,
+ style : 'padding-left: 155px',
+ items : [
+ {
+ xtype : 'treepanel',
+ name : 'mirror-url-list',
+ title : 'Mirror URLs',
+ border : true,
+ bodyBorder : true,
+ bodyStyle : 'background-color:#FFFFFF; border: 1px solid #B5B8C8',
+ style : 'padding: 0 20px 0 0',
+ width : 275,
+ height : 300,
+ animate : true,
+ lines : false,
+ autoScroll : true,
+ containerScroll : true,
+ rootVisible : false,
+ ddScroll : true,
+ enableDD : true,
+ root : new Ext.tree.TreeNode({
+ text : 'root',
+ draggable : false
+ })
+ },
+ {
+ xtype : 'panel',
+ width : 120,
+ items : [
+ {
+ xtype : 'button',
+ text : 'Remove',
+ style : 'padding-left: 6px',
+ minWidth : 100,
+ id : 'button-remove',
+ handler : this.removeMirrorUrl,
+ scope : this
+ },
+ {
+ xtype : 'button',
+ text : 'Remove All',
+ style : 'padding-left: 6px; margin-top: 5px',
+ minWidth : 100,
+ id : 'button-remove-all',
+ handler : this.removeAllMirrorUrls,
+ scope : this
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ });
+ }
+}, function() {
+ Sonatype.Events.addListener('repositoryViewInit', function(cardPanel, rec) {
+ var sp = Sonatype.lib.Permissions;
+ if (rec.data.resourceURI && sp.checkPermission('nexus:repositorymirrors', sp.READ) && rec.data.userManaged)
+ {
+ if (rec.data.repoType === 'proxy')
+ {
+ cardPanel.add(new Sonatype.repoServer.ProxyMirrorEditor({
+ payload : rec,
+ tabTitle : 'Mirrors',
+ name : 'mirrors'
+ }));
+ }
+ }
+ });
+});
+
+
23 nexus-webapp/src/main/webapp/js/repoServer.js
View
@@ -13,15 +13,12 @@
/*global define*/
define('repoServer',
[
- 'repoServer/RepoServer',
'Nexus/repository/AbstractRepoPanel',
- 'Sonatype/repoServer/ArtifactContainer',
'repoServer/FeedViewPanel',
- 'repoServer/Maven2InformationPanel',
- 'repoServer/MirrorConfigPanel',
'repoServer/PrivilegeEditPanel',
'repoServer/RepoEditPanel',
'repoServer/RepoMaintPanel',
+ 'repoServer/RepoServer',
'repoServer/RepositoryBrowserContainer',
'repoServer/RepoSummaryPanel',
'repoServer/RepoTargetEditPanel',
@@ -31,19 +28,23 @@ define('repoServer',
'repoServer/SchedulesEditPanel',
'repoServer/UserBrowserPanel',
'repoServer/UserPrivilegeBrowserPanel',
- 'Sonatype/repoServer/RepositoryRoutingPanel',
- 'Sonatype/repoServer/ServerEditPanel',
+ 'Sonatype/repoServer/ArtifactContainer',
+ 'Sonatype/repoServer/ArtifactUploadPanel',
'Sonatype/repoServer/ConfigViewPanel',
'Sonatype/repoServer/DefaultUserEditor',
'Sonatype/repoServer/Documentation',
- 'Sonatype/repoServer/UserEditPanel',
- 'Sonatype/repoServer/UserMappingEditor',
- 'Sonatype/repoServer/referenceData',
- 'Sonatype/repoServer/RepositoryGroupEditor',
'Sonatype/repoServer/HelpAboutPanel',
+ 'Sonatype/repoServer/HostedMirrorEditor',
'Sonatype/repoServer/LogEditPanel',
'Sonatype/repoServer/LogsViewPanel',
- 'Sonatype/repoServer/ArtifactUploadPanel'
+ 'Sonatype/repoServer/Maven2InformationPanel',
+ 'Sonatype/repoServer/ProxyMirrorEditor',
+ 'Sonatype/repoServer/referenceData',
+ 'Sonatype/repoServer/RepositoryGroupEditor',
+ 'Sonatype/repoServer/RepositoryRoutingPanel',
+ 'Sonatype/repoServer/ServerEditPanel',
+ 'Sonatype/repoServer/UserEditPanel',
+ 'Sonatype/repoServer/UserMappingEditor'
], function() {
}
176 nexus-webapp/src/main/webapp/js/repoServer/Maven2InformationPanel.js
View
@@ -1,176 +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.
- */
-/*global define*/
-define('repoServer/Maven2InformationPanel',['extjs', 'Sonatype/all'], function(Ext, Sonatype){
-Sonatype.repoServer.Maven2InformationPanel = function(config) {
- Ext.apply(this, config || {}, {
- halfSize : false
- });
-
- this.sp = Sonatype.lib.Permissions;
-
- this.linkDivId = Ext.id();
- this.linkLabelId = Ext.id();
-
- Sonatype.repoServer.Maven2InformationPanel.superclass.constructor.call(this, {
- title : 'Maven',
- autoScroll : true,
- border : true,
- frame : true,
- collapsible : false,
- collapsed : false,
- items : [{
- xtype : 'displayfield',
- fieldLabel : 'Group',
- name : 'groupId',
- anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
- allowBlank : true,
- readOnly : true
- }, {
- xtype : 'displayfield',
- fieldLabel : 'Artifact',
- name : 'artifactId',
- anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
- allowBlank : true,
- readOnly : true
- }, {
- xtype : 'displayfield',
- fieldLabel : 'Version',
- name : 'baseVersion',
- anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
- allowBlank : true,
- readOnly : true
- }, {
- xtype : 'displayfield',
- fieldLabel : 'Classifier',
- name : 'classifier',
- anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
- allowBlank : true,
- readOnly : true
- }, {
- xtype : 'displayfield',
- fieldLabel : 'Extension',
- name : 'extension',
- anchor : Sonatype.view.FIELD_OFFSET_WITH_SCROLL,
- allowBlank : true,
- readOnly : true
- }, {
- xtype : 'textarea',
- fieldLabel : 'XML',
- anchor : Sonatype.view.FIELD_OFFSET,
- height : 120,
- name : 'dependencyXmlChunk',
- allowBlank : true,
- readOnly : true
- }]
- });
-};
-
-Ext.extend(Sonatype.repoServer.Maven2InformationPanel, Ext.form.FormPanel, {
-
- showArtifact : function(data, artifactContainer) {
- this.data = data;
- if (data) {
- Ext.Ajax.request({
- url : this.data.resourceURI + '?describe=maven2&isLocal=true',
- callback : function(options, isSuccess, response) {
- if (isSuccess)
- {
- var infoResp = Ext.decode(response.responseText);
-
- // hide classifier if empty
- if (this.data.classifier)
- {
- this.find('name', 'classifier')[0].show();
- }
- else
- {
- this.find('name', 'classifier')[0].hide();
- }
- this.form.setValues(infoResp.data);
- artifactContainer.showTab(this);
- }
- else
- {
- if (response.status === 404)
- {
- artifactContainer.hideTab(this);
- }
- else
- {
- Sonatype.utils.connectionError(response, 'Unable to retrieve Maven information.');
- }
- }
- },
- scope : this,
- method : 'GET',
- suppressStatus : '404'
- });
- }
- else
- {
- this.find('name', 'groupId')[0].setRawValue(null);
- this.find('name', 'artifactId')[0].setRawValue(null);
- this.find('name', 'baseVersion')[0].setRawValue(null);
- this.find('name', 'classifier')[0].setRawValue(null);
- this.find('name', 'extension')[0].setRawValue(null);
- this.find('name', 'dependencyXmlChunk')[0].setRawValue(null);
- }
- }
- });
-
-Sonatype.Events.addListener('fileContainerInit', function(items) {
- items.push(new Sonatype.repoServer.Maven2InformationPanel({
- name : 'maven2InformationPanel',
- tabTitle : 'Maven',
- preferredIndex : 10
- }));
- });
-
-Sonatype.Events.addListener('fileContainerUpdate', function(artifactContainer, data) {
- var panel = artifactContainer.find('name', 'maven2InformationPanel')[0];
-
- if ( data && data.leaf)
- {
- panel.showArtifact(data, artifactContainer);
- }
- else
- {
- panel.showArtifact(null, artifactContainer);
- }
- });
-
-Sonatype.Events.addListener('artifactContainerInit', function(items) {
- items.push(new Sonatype.repoServer.Maven2InformationPanel({
- name : 'maven2InformationPanel',
- tabTitle : 'Maven',
- preferredIndex : 10
- }));
- });
-
-Sonatype.Events.addListener('artifactContainerUpdate', function(artifactContainer, payload) {
- var panel = artifactContainer.find('name', 'maven2InformationPanel')[0];
-
- if (payload && payload.leaf)
- {
- panel.showArtifact(payload, artifactContainer);
- }
- else
- {
- panel.showArtifact(null, artifactContainer);
- }
-
- });
-
-});
-
558 nexus-webapp/src/main/webapp/js/repoServer/MirrorConfigPanel.js
View
@@ -1,558 +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.
- */
-/*global define*/
-define('repoServer/MirrorConfigPanel',['extjs', 'Sonatype/all'], function(Ext, Sonatype){
-var MIRROR_URL_REGEXP = /^(?:http|https):\/\//i;
-
-Sonatype.repoServer.AbstractMirrorPanel = function(config) {
- this.mirrorStatusTask = {
- run : function() {
- Ext.Ajax.request({
- url : Sonatype.config.repos.urls.repoMirrorStatus + '/' + this.payload.data.id,
- callback : this.statusCallback,
- scope : this
- });
- },
- interval : 5000, // poll every 5 seconds
- scope : this
- };
-
- this.mirrorRecordConstructor = Ext.data.Record.create([{
- name : 'id'
- }, {
- name : 'url',
- sortType : Ext.data.SortTypes.asUCString
- }]);
-
- this.mirrorReader = new Ext.data.JsonReader({
- root : 'data',
- id : 'id'
- }, this.mirrorRecordConstructor);
-
- this.mirrorDataStore = new Ext.data.Store({
- url : Sonatype.config.repos.urls.repoMirrors + '/' + this.payload.data.id,
- reader : this.mirrorReader,
- sortInfo : {
- field : 'url',
- direction : 'ASC'
- },
- autoLoad : false
- });
-
- var defaultConfig = {
- uri : Sonatype.config.repos.urls.repoMirrors + '/' + this.payload.data.id,
- referenceData : Sonatype.repoServer.referenceData.repoMirrors,
- dataStores : [this.mirrorDataStore],
- dataModifiers : {
- load : {
- 'rootData' : this.loadMirrors.createDelegate(this)
- },
- submit : {
- 'rootData' : this.saveMirrors.createDelegate(this)
- }
- },
- listeners : {
- submit : {
- fn : this.submitHandler,
- scope : this
- },
- activate : {
- fn : this.activateHandler,
- scope : this
- },
- deactivate : {
- fn : this.deactivateHandler,
- scope : this
- },
- destroy : {
- fn : this.destroyHandler,
- scope : this
- }
- }
- };
-
- Ext.apply(this, config || {}, defaultConfig);
-
- Sonatype.repoServer.AbstractMirrorPanel.superclass.constructor.call(this, {});
-};
-
-Ext.extend(Sonatype.repoServer.AbstractMirrorPanel, Sonatype.ext.FormPanel, {
- addNewMirrorUrl : function() {
- var
- i,
- treePanel = this.find('name', 'mirror-url-list')[0],
- nodes = treePanel.root.childNodes,
- urlField = this.find('name', 'mirrorUrl')[0],
- url = urlField.getRawValue();
-
- if (urlField.isValid() && url)
- {
- for (i = 0; i < nodes.length; i+=1)
- {
- if (url === nodes[i].attributes.payload.url)
- {
- urlField.markInvalid('This URL already exists');
- return;
- }
- }
-
- urlField.clearInvalid();
-
- this.addUrlNode(treePanel, url, url, Sonatype.config.extPath + '/resources/images/default/tree/leaf.gif');
- urlField.setRawValue('');
- urlField.setValue('');
- }
- },
-
- addUrlNode : function(treePanel, url, id, icon) {
- var validId, manualUrl;
- if (url === id)
- {
- validId = Ext.id();
- manualUrl = true;
- }
- else
- {
- validId = id;
- manualUrl = false;
- }
- treePanel.root.appendChild(new Ext.tree.TreeNode({
- id : id,
- text : url,
- href : url,
- hrefTarget : '_new',
- payload : {
- id : manualUrl ? '' : id,
- url : url
- },
- allowChildren : false,
- draggable : true,
- leaf : true,
- icon : icon
- }));
- },
-
- removeMirrorUrl : function() {
- var
- treePanel = this.find('name', 'mirror-url-list')[0],
- selectedNode = treePanel.getSelectionModel().getSelectedNode();
-
- if (selectedNode)
- {
- treePanel.root.removeChild(selectedNode);
- }
- },
-
- removeAllMirrorUrls : function() {
- var
- treePanel = this.find('name', 'mirror-url-list')[0],
- treeRoot = treePanel.root;
-
- while (treeRoot.lastChild)
- {
- treeRoot.removeChild(treeRoot.lastChild);
- }
- },
-
- loadMirrors : function(arr, srcObj, fpanel) {
- var
- i, j, childNodes, found,
- treePanel = this.find('name', 'mirror-url-list')[0],
- mirrorArray = [];
-
- for (i = 0; i < arr.length; i+=1)
- {
- childNodes = treePanel.getRootNode().childNodes;
- found = false;
- if (childNodes && childNodes.length)
- {
- for (j = 0; j < childNodes.length; j+=1)
- {
- if (arr[i].id === childNodes[j].id)
- {
- mirrorArray[i] = {
- id : arr[i].id,
- url : arr[i].url,
- icon : childNodes[j].ui.iconNode.src
- };
- found = true;
- break;
- }
- }
- }
- if (!found)
- {
- mirrorArray[i] = {
- id : arr[i].id,
- url : arr[i].url,
- icon : Sonatype.config.extPath + '/resources/images/default/tree/leaf.gif'
- };
- }
- }
-