Browse files

Merge branch 'master' into NEXUS-5662-userid-mdc

Conflicts:
	plugins/siesta/nexus-siesta-plugin/src/main/java/org/sonatype/nexus/plugins/siesta/SiestaModule.java
  • Loading branch information...
2 parents 9ce6299 + 5ff39ca commit 4c41118e3b9fd11ba522e3ba8956ed83c4e7a176 @jdillon jdillon committed Apr 15, 2013
Showing with 1,368 additions and 353 deletions.
  1. +9 −1 nexus-core/src/main/java/org/sonatype/nexus/AbstractApplicationStatusSource.java
  2. +57 −0 nexus-core/src/main/java/org/sonatype/nexus/guice/AbstractInterceptorModule.java
  3. +37 −0 nexus-core/src/main/java/org/sonatype/nexus/guice/FilterChainModule.java
  4. +2 −3 nexus-core/src/main/java/org/sonatype/nexus/guice/NexusModules.java
  5. +11 −6 nexus-core/src/main/java/org/sonatype/nexus/plugins/DefaultNexusPluginManager.java
  6. +8 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/AbstractChecksumContentValidator.java
  7. +1 −1 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumContentValidator.java
  8. +18 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumPolicy.java
  9. +4 −3 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperation.java
  10. +7 −6 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/MetadataUtil.java
  11. +12 −0 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/FilePrefixSource.java
  12. +9 −1 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/ManagerImpl.java
  13. +1 −1 ...re/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/RemoteContentDiscovererImpl.java
  14. +5 −2 ...-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategy.java
  15. +12 −8 ...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. +45 −0 nexus-core/src/main/java/org/sonatype/nexus/security/FilterChain.java
  19. +6 −2 nexus-core/src/main/java/org/sonatype/nexus/timing/TimingModule.java
  20. +55 −54 nexus-core/src/main/resources/META-INF/security/security.xml
  21. +4 −1 nexus-core/src/test/java/org/sonatype/nexus/plugins/DefaultNexusPluginManagerTest.java
  22. +104 −0 ...core/src/test/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperationTest.java
  23. +44 −0 ...e/src/test/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategyTest.java
  24. +216 −0 .../java/org/sonatype/nexus/proxy/storage/local/fs/NEXUS5612DefaultFSLocalRepositoryStorageTest.java
  25. +6 −5 nexus-oss-webapp/src/main/resources/content/bin/jsw/conf/wrapper.conf
  26. +2 −0 ...ness-launcher/src/main/java/org/sonatype/nexus/integrationtests/AbstractNexusIntegrationTest.java
  27. +75 −0 nexus-web-utils/src/main/java/org/sonatype/nexus/web/FilterChainInstaller.java
  28. +8 −6 nexus-webapp/src/main/webapp/js/NX/base.js
  29. +0 −5 nexus-webapp/src/main/webapp/js/Nexus/config.js
  30. +0 −1 nexus-webapp/src/main/webapp/js/Nexus/ext.js
  31. +4 −2 nexus-webapp/src/main/webapp/js/Nexus/profile/Summary.js
  32. +4 −2 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/DefaultUserEditor.js
  33. +1 −0 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/Maven2InformationPanel.js
  34. +38 −15 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/ServerEditPanel.js
  35. +24 −7 nexus-webapp/src/main/webapp/js/ext/form/field.js
  36. +3 −1 nexus-webapp/src/main/webapp/js/lib/string.js
  37. +1 −1 nexus-webapp/src/main/webapp/js/repoServer.js
  38. +0 −5 nexus-webapp/src/main/webapp/js/repoServer/RepoServer.js
  39. +2 −1 nexus-webapp/src/main/webapp/style/Sonatype.css
  40. +5 −0 plugins/nexus-timeline-plugin/pom.xml
  41. +50 −0 plugins/nexus-timeline-plugin/src/main/java/org/sonatype/nexus/rest/ui/TimelineUiContributor.java
  42. +91 −68 ...p/js/Nexus/ext/feedgrid.js → plugins/nexus-timeline-plugin/src/main/js/Nexus/timeline/FeedGrid.js
  43. +57 −42 ...epoServer/FeedViewPanel.js → plugins/nexus-timeline-plugin/src/main/js/Nexus/timeline/FeedView.js
  44. +19 −0 plugins/nexus-timeline-plugin/src/main/js/nexus-timeline-plugin-boot.js
  45. +252 −94 ...stlet1x-plugin/src/main/java/org/sonatype/nexus/rest/artifact/AbstractArtifactPlexusResource.java
  46. +3 −3 ...t1x/nexus-restlet1x-plugin/src/main/java/org/sonatype/nexus/rest/artifact/PomArtifactManager.java
  47. +1 −1 ...-plugin/src/main/java/org/sonatype/nexus/security/filter/authc/NexusHttpAuthenticationFilter.java
  48. +37 −1 ...s-siesta-plugin/src/main/java/org/sonatype/nexus/plugins/siesta/AuthorizationExceptionMapper.java
  49. +11 −0 plugins/siesta/nexus-siesta-plugin/src/main/java/org/sonatype/nexus/plugins/siesta/SiestaModule.java
View
10 nexus-core/src/main/java/org/sonatype/nexus/AbstractApplicationStatusSource.java
@@ -71,7 +71,7 @@ protected ReadWriteLock getLock()
// ==
/**
- * Returns the SystemStatus, guaranteeing it's consistent state.
+ * Returns the SystemStatus, guaranteeing its consistent state.
*/
public SystemStatus getSystemStatus()
{
@@ -91,6 +91,14 @@ public SystemStatus getSystemStatus()
}
}
+ /**
+ * Force an update of SystemStatus.
+ */
+ public void updateSystemStatus()
+ {
+ updateSystemStatusIfNeeded( true );
+ }
+
public boolean setState( SystemState state )
{
Lock lock = getLock().writeLock();
View
57 nexus-core/src/main/java/org/sonatype/nexus/guice/AbstractInterceptorModule.java
@@ -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 );
+ }
+}
View
37 nexus-core/src/main/java/org/sonatype/nexus/guice/FilterChainModule.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.sonatype.nexus.security.FilterChain;
+import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
+
+/**
+ * Support module for configuring {@link FilterChain}s.
+ *
+ * @since 2.5
+ */
+public abstract class FilterChainModule
+ extends AbstractModule
+{
+
+ protected void addFilterChain( final String pathPattern, final String filterExpression )
+ {
+ bind( FilterChain.class )
+ .annotatedWith( Names.named( pathPattern ) )
+ .toInstance( new FilterChain( pathPattern, filterExpression )
+ );
+ }
+
+}
View
5 nexus-core/src/main/java/org/sonatype/nexus/guice/NexusModules.java
@@ -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());
}
}
View
17 nexus-core/src/main/java/org/sonatype/nexus/plugins/DefaultNexusPluginManager.java
@@ -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,14 +106,16 @@ 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 );
this.repositoryManager = checkNotNull( repositoryManager );
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 )
{
View
8 ...s-core/src/main/java/org/sonatype/nexus/proxy/maven/AbstractChecksumContentValidator.java
@@ -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
View
2 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumContentValidator.java
@@ -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
View
18 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/ChecksumPolicy.java
@@ -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 );
View
7 .../src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperation.java
@@ -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;
}
View
13 ...s-core/src/main/java/org/sonatype/nexus/proxy/maven/metadata/operations/MetadataUtil.java
@@ -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() );
}
}
View
12 ...-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/FilePrefixSource.java
@@ -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 )
View
10 nexus-core/src/main/java/org/sonatype/nexus/proxy/maven/routing/internal/ManagerImpl.java
@@ -179,6 +179,8 @@ public ManagerImpl( final EventBus eventBus, final ApplicationStatusSource appli
this.eventBus.register( this );
}
+ private volatile boolean periodicUpdaterDidRunAtLeastOnce = false;
+
@Override
public void startup()
{
@@ -224,6 +226,7 @@ public void run()
return;
}
mayUpdateAllProxyPrefixFiles();
+ periodicUpdaterDidRunAtLeastOnce = true;
}
}, 0L /*no initial delay*/, TimeUnit.HOURS.toMillis( 1 ), TimeUnit.MILLISECONDS );
@@ -524,6 +527,11 @@ protected boolean doUpdatePrefixFileAsync( final boolean forced, final MavenRepo
@VisibleForTesting
public boolean isUpdatePrefixFileJobRunning()
{
+ if ( config.isFeatureActive() && !periodicUpdaterDidRunAtLeastOnce )
+ {
+ getLogger().debug( "Boot process not done yet, periodic updater did not yet finish!" );
+ return true;
+ }
final Statistics statistics = constrainedExecutor.getStatistics();
getLogger().debug( "Running update jobs for {}", statistics.getCurrentlyRunningJobKeys() );
return !statistics.getCurrentlyRunningJobKeys().isEmpty();
@@ -570,7 +578,7 @@ else if ( mavenRepository.getRepositoryKind().isFacetAvailable( MavenHostedRepos
catch ( IllegalStateException e )
{
// just ack it, log it and return peacefully
- getLogger().info( e.getMessage() );
+ getLogger().info( "Maven repository {} not in state for prefix file update: {}", mavenRepository, e.getMessage() );
return;
}
}
View
2 ...ain/java/org/sonatype/nexus/proxy/maven/routing/internal/RemoteContentDiscovererImpl.java
@@ -103,7 +103,7 @@ public RemoteContentDiscovererImpl( final List<RemoteStrategy> remoteStrategies
}
catch ( Exception e )
{
- getLogger().warn( "Remote strategy {} error: {}", strategy.getId(), e.getMessage() );
+ getLogger().warn( "Remote strategy {} error", strategy.getId(), e );
discoveryResult.recordError( strategy.getId(), e );
break;
}
View
7 ...c/main/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategy.java
@@ -29,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;
@@ -124,7 +125,7 @@ public StrategyResult discover( final MavenProxyRepository mavenProxyRepository
if ( prefixFileAgeInDays < 1 )
{
return new StrategyResult( "Remote publishes prefix file (is less than a day old), using it.",
- prefixSource, true );
+ prefixSource, true );
}
else
{
@@ -147,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
View
20 .../main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSLocalRepositoryStorage.java
@@ -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;
}
View
5 nexus-core/src/main/java/org/sonatype/nexus/proxy/storage/local/fs/DefaultFSPeer.java
@@ -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;
View
6 nexus-core/src/main/java/org/sonatype/nexus/scheduling/AbstractNexusRepositoriesTask.java
@@ -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 + "'!" );
}
}
View
45 nexus-core/src/main/java/org/sonatype/nexus/security/FilterChain.java
@@ -0,0 +1,45 @@
+/*
+ * 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.security;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A Shiro filter chain (mapping between a path pattern and a filter expression).
+ *
+ * @since 2.5
+ */
+public class FilterChain
+{
+
+ private final String pathPattern;
+
+ private final String filterExpression;
+
+ public FilterChain( final String pathPattern, final String filterExpression )
+ {
+ this.pathPattern = checkNotNull( pathPattern );
+ this.filterExpression = checkNotNull( filterExpression );
+ }
+
+ public String getPathPattern()
+ {
+ return pathPattern;
+ }
+
+ public String getFilterExpression()
+ {
+ return filterExpression;
+ }
+
+}
View
8 nexus-core/src/main/java/org/sonatype/nexus/timing/TimingModule.java
@@ -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() {
View
109 nexus-core/src/main/resources/META-INF/security/security.xml
@@ -16,58 +16,59 @@
<!-- Default nexus security configuration -->
<!-- used as default config source -->
<security>
- <version>2.0.5</version>
- <users>
- <user>
- <id>admin</id>
- <firstName>Administrator</firstName>
- <!-- admin123 -->
- <password>f865b53623b121fd34ee5426c792e5c33af8c227</password>
- <status>active</status>
- <email>changeme@yourcompany.com</email>
- </user>
- <user>
- <id>deployment</id>
- <firstName>Deployment</firstName>
- <lastName>User</lastName>
- <!-- deployment123 -->
- <password>b2a0e378437817cebdf753d7dff3dd75483af9e0</password>
- <status>active</status>
- <email>changeme1@yourcompany.com</email>
- </user>
- <user>
- <id>anonymous</id>
- <firstName>Nexus</firstName>
- <lastName>Anonymous User</lastName>
- <!-- anonymous -->
- <password>0a92fab3230134cca6eadd9898325b9b2ae67998</password>
- <status>active</status>
- <email>changeme2@yourcompany.com</email>
- </user>
- </users>
- <userRoleMappings>
- <userRoleMapping>
- <userId>admin</userId>
- <source>default</source>
- <roles>
- <role>nx-admin</role>
- </roles>
- </userRoleMapping>
- <userRoleMapping>
- <userId>deployment</userId>
- <source>default</source>
- <roles>
- <role>nx-deployment</role>
- <role>repository-any-full</role>
- </roles>
- </userRoleMapping>
- <userRoleMapping>
- <userId>anonymous</userId>
- <source>default</source>
- <roles>
- <role>anonymous</role>
- <role>repository-any-read</role>
- </roles>
- </userRoleMapping>
- </userRoleMappings>
+ <version>2.0.5</version>
+ <users>
+ <user>
+ <id>admin</id>
+ <firstName>Administrator</firstName>
+ <lastName>User</lastName>
+ <!-- admin123 -->
+ <password>f865b53623b121fd34ee5426c792e5c33af8c227</password>
+ <status>active</status>
+ <email>changeme@yourcompany.com</email>
+ </user>
+ <user>
+ <id>deployment</id>
+ <firstName>Deployment</firstName>
+ <lastName>User</lastName>
+ <!-- deployment123 -->
+ <password>b2a0e378437817cebdf753d7dff3dd75483af9e0</password>
+ <status>active</status>
+ <email>changeme1@yourcompany.com</email>
+ </user>
+ <user>
+ <id>anonymous</id>
+ <firstName>Nexus</firstName>
+ <lastName>Anonymous User</lastName>
+ <!-- anonymous -->
+ <password>0a92fab3230134cca6eadd9898325b9b2ae67998</password>
+ <status>active</status>
+ <email>changeme2@yourcompany.com</email>
+ </user>
+ </users>
+ <userRoleMappings>
+ <userRoleMapping>
+ <userId>admin</userId>
+ <source>default</source>
+ <roles>
+ <role>nx-admin</role>
+ </roles>
+ </userRoleMapping>
+ <userRoleMapping>
+ <userId>deployment</userId>
+ <source>default</source>
+ <roles>
+ <role>nx-deployment</role>
+ <role>repository-any-full</role>
+ </roles>
+ </userRoleMapping>
+ <userRoleMapping>
+ <userId>anonymous</userId>
+ <source>default</source>
+ <roles>
+ <role>anonymous</role>
+ <role>repository-any-read</role>
+ </roles>
+ </userRoleMapping>
+ </userRoleMappings>
</security>
View
5 nexus-core/src/test/java/org/sonatype/nexus/plugins/DefaultNexusPluginManagerTest.java
@@ -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
View
104 .../test/java/org/sonatype/nexus/proxy/maven/metadata/operations/AddPluginOperationTest.java
@@ -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" ) );
+ }
+}
View
44 ...st/java/org/sonatype/nexus/proxy/maven/routing/internal/RemotePrefixFileStrategyTest.java
@@ -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();
+ }
+ }
+
}
View
216 ...g/sonatype/nexus/proxy/storage/local/fs/NEXUS5612DefaultFSLocalRepositoryStorageTest.java
@@ -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 ) ) );
+ }
+ }
+ }
+}
View
11 nexus-oss-webapp/src/main/resources/content/bin/jsw/conf/wrapper.conf
@@ -26,11 +26,12 @@ wrapper.java.classpath.3=./conf/
wrapper.java.library.path.1=bin/jsw/lib
# Additional JVM parameters (tune if needed, but match the sequence of numbers!)
-#wrapper.java.additional.1=-Xdebug
-#wrapper.java.additional.2=-Xnoagent
-#wrapper.java.additional.3=-Djava.compiler=NONE
-#wrapper.java.additional.4=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-#wrapper.java.additional.5=-XX:+HeapDumpOnOutOfMemoryError
+wrapper.java.additional.1=-Djava.net.preferIPv4Stack=true
+#wrapper.java.additional.2=-Xdebug
+#wrapper.java.additional.3=-Xnoagent
+#wrapper.java.additional.4=-Djava.compiler=NONE
+#wrapper.java.additional.5=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#wrapper.java.additional.6=-XX:+HeapDumpOnOutOfMemoryError
wrapper.app.parameter.1=./conf/jetty.xml
View
2 ...ncher/src/main/java/org/sonatype/nexus/integrationtests/AbstractNexusIntegrationTest.java
@@ -164,6 +164,8 @@
// guice finalizer turned OFF
System.setProperty( "guice.executor.class", "NONE" );
+ // NEXUS-5660 test with ipv4 as app runs with this set now
+ System.setProperty( "java.net.preferIPv4Stack", "true" );
}
static
View
75 nexus-web-utils/src/main/java/org/sonatype/nexus/web/FilterChainInstaller.java
@@ -0,0 +1,75 @@
+/*
+ * 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.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import org.sonatype.inject.EagerSingleton;
+import org.sonatype.nexus.proxy.events.NexusStartedEvent;
+import org.sonatype.nexus.proxy.events.NexusStoppedEvent;
+import org.sonatype.nexus.security.FilterChain;
+import org.sonatype.security.web.ProtectedPathManager;
+import org.sonatype.sisu.goodies.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+/**
+ * Installs configured {@link FilterChain}s with {@link ProtectedPathManager}.
+ *
+ * @since 2.5
+ */
+@Named
+@EagerSingleton
+public class FilterChainInstaller
+{
+
+ private final EventBus eventBus;
+
+ private final Provider<ProtectedPathManager> protectedPathManager;
+
+ private final List<FilterChain> filterChains;
+
+ @Inject
+ public FilterChainInstaller( final EventBus eventBus,
+ final Provider<ProtectedPathManager> protectedPathManager,
+ final List<FilterChain> filterChains )
+ {
+ this.eventBus = checkNotNull( eventBus );
+ this.protectedPathManager = checkNotNull( protectedPathManager );
+ this.filterChains = checkNotNull( filterChains );
+
+ eventBus.register( this );
+ }
+
+ @Subscribe
+ public void onEvent( final NexusStartedEvent event )
+ {
+ for ( final FilterChain filterChain : filterChains )
+ {
+ protectedPathManager.get().addProtectedResource(
+ filterChain.getPathPattern(), filterChain.getFilterExpression()
+ );
+ }
+ }
+
+ @Subscribe
+ public void onEvent( final NexusStoppedEvent event )
+ {
+ eventBus.unregister( this );
+ }
+
+}
View
14 nexus-webapp/src/main/webapp/js/NX/base.js
@@ -46,12 +46,14 @@ define('NX/base', ['require'], function(require) {
*/
obj: function (path) {
var context = NX.global;
- Ext.each(path.split('.'), function (part) {
- context = context[part];
- if (context === undefined) {
- throw new Error('No object at path: ' + path + '; part is undefined: ' + part);
- }
- });
+ if (typeof path === 'string') {
+ Ext.each(path.split('.'), function (part) {
+ context = context[part];
+ if (context === undefined) {
+ throw new Error('No object at path: ' + path + '; part is undefined: ' + part);
+ }
+ });
+ }
return context;
},
View
5 nexus-webapp/src/main/webapp/js/Nexus/config.js
@@ -90,11 +90,6 @@ define('Nexus/config',['extjs', 'Nexus/messagebox', 'Sonatype/init', 'Nexus/conf
configCurrent : servicePath + '/configs/current',
logs : servicePath + '/logs',
logConfig : servicePath + '/log/config',
- feeds : servicePath + '/feeds',
- recentlyChangedArtifactsRss : servicePath + '/feeds/recentChanges',
- recentlyCachedArtifactsRss : servicePath + '/feeds/recentlyCached',
- recentlyDeployedArtifactsRss : servicePath + '/feeds/recentlyDeployed',
- systemChangesRss : servicePath + '/feeds/systemChanges',
status : servicePath + '/status',
schedules : servicePath + '/schedules',
scheduleRun : servicePath + '/schedule_run',
View
1 nexus-webapp/src/main/webapp/js/Nexus/ext.js
@@ -13,7 +13,6 @@
/*global define*/
define('Nexus/ext',
[
- 'Nexus/ext/feedgrid',
'Nexus/ext/FormPanel',
'Nexus/ext/TextEntryList',
'Nexus/ext/linkbutton',
View
6 nexus-webapp/src/main/webapp/js/Nexus/profile/Summary.js
@@ -58,7 +58,8 @@ Nexus.profile.Summary = function(config) {
labelStyle : 'margin-left: 15px; width: 185px;',
helpText : ht.firstName,
name : 'firstName',
- allowBlank : true,
+ allowBlank : false,
+ itemCls : 'required-field',
width : this.FIELD_WIDTH,
disabled : isExternalUser,
validator : function(v) {
@@ -76,7 +77,8 @@ Nexus.profile.Summary = function(config) {
labelStyle : 'margin-left: 15px; width: 185px;',
helpText : ht.lastName,
name : 'lastName',
- allowBlank : true,
+ allowBlank : false,
+ itemCls : 'required-field',
width : this.FIELD_WIDTH,
disabled : isExternalUser,
validator : function(v) {
View
6 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/DefaultUserEditor.js
@@ -77,7 +77,8 @@ NX.define('Sonatype.repoServer.DefaultUserEditor', {
labelStyle : 'margin-left: 15px; width: 185px;',
helpText : ht.firstName,
name : 'firstName',
- allowBlank : true,
+ allowBlank : false,
+ itemCls : 'required-field',
htmlDecode : true,
width : this.COMBO_WIDTH,
validator : function(v) {
@@ -94,7 +95,8 @@ NX.define('Sonatype.repoServer.DefaultUserEditor', {
labelStyle : 'margin-left: 15px; width: 185px;',
helpText : ht.lastName,
name : 'lastName',
- allowBlank : true,
+ allowBlank : false,
+ itemCls : 'required-field',
htmlDecode : true,
width : this.COMBO_WIDTH,
validator : function(v) {
View
1 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/Maven2InformationPanel.js
@@ -13,6 +13,7 @@
/*global NX, Sonatype, Ext*/
NX.define('Sonatype.repoServer.Maven2InformationPanel', {
extend : 'Ext.form.FormPanel',
+ requirejs : ['Sonatype/init'],
constructor : function(config) {
Ext.apply(this, config || {}, {
View
53 nexus-webapp/src/main/webapp/js/Sonatype/repoServer/ServerEditPanel.js
@@ -11,6 +11,13 @@
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
/*global Ext, Sonatype, Nexus*/
+/**
+ * The server settings view.
+ *
+ * @event serverConfigViewPostInit
+ * Global event for plugins to change the server settings view. (Uses Sonatype.Events.)
+ * @param {Ext.FormPanel} The form panel used as the server settings view.
+ */
Ext.define('Sonatype.repoServer.ServerEditPanel', {
extend : 'Ext.Panel',
requires : 'Nexus.util.Strings',
@@ -80,12 +87,12 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
layoutConfig : {
labelSeparator : ''
},
-
items : [
{
xtype : 'fieldset',
checkboxToggle : false,
title : 'SMTP Settings',
+ name : 'smtp-settings',
anchor : Sonatype.view.FIELDSET_OFFSET,
collapsible : true,
autoHeight : true,
@@ -149,14 +156,17 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
anchor : Sonatype.view.FIELD_OFFSET,
allowBlank : false,
itemCls : 'required-field'
- },
+ }
+ ],
+ buttons : [
{
xtype : 'button',
scope : this,
text : 'Test SMTP settings',
handler : this.testSmtpBtnHandler
}
- ]
+ ],
+ buttonAlign : 'left'
},
{
xtype : 'fieldset',
@@ -687,6 +697,8 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
securityConfigField = this.formPanel.find('name', 'securityEnabled')[0];
securityConfigField.on('select', this.securitySelectHandler, securityConfigField);
+
+ Sonatype.Events.fireEvent('serverConfigViewPostInit', this.formPanel);
},
optionalFieldsetExpandHandler : function(panel) {
@@ -870,9 +882,21 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
formBind : true,
scope : this,
handler : function() {
- var email = w.find('name', 'email')[0].getValue();
- this.runStmpConfigCheck(email, data);
- w.close();
+ var email = w.find('name', 'email')[0].getValue(),
+ mask = new Ext.LoadMask(w.el, {
+ msg : 'Validating SMTP settings...',
+ removeMask : true
+ });
+
+ mask.show();
+
+ this.runStmpConfigCheck(email, data, function(success) {
+ mask.hide();
+
+ if (success) {
+ w.close();
+ }
+ });
}
},
{
@@ -891,7 +915,7 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
w.show();
},
- runStmpConfigCheck : function(testEmail, data) {
+ runStmpConfigCheck : function(testEmail, data, callback) {
data.testEmail = testEmail;
@@ -901,20 +925,19 @@ Ext.define('Sonatype.repoServer.ServerEditPanel', {
jsonData : {
data : data
},
- callback : function(options, success, response) {
- this.el.unmask();
-
- if (success) {
+ callback : function(options, success) {
+ callback(success);
+ },
+ success : function() {
Sonatype.MessageBox.show({
title : 'SMTP configuration',
msg : 'SMTP configuration validated successfully, check your inbox!',
buttons : Sonatype.MessageBox.OK,
icon : Sonatype.MessageBox.INFO
});
- }
- else {
- Sonatype.utils.connectionError(response, 'Error on SMTP validation!');
- }
+ },
+ failure : function(response) {
+ Sonatype.utils.connectionError(response, 'Error on SMTP validation!');
},
scope : this
});
View
31 nexus-webapp/src/main/webapp/js/ext/form/field.js
@@ -16,10 +16,12 @@ Ext.override(Ext.form.Field, {
/*
* Override default form field rendering to include help text quick tip on
* question mark rendered after field label.
+ * Specifying 'helpText' will cause a help icon and text to be added as a QuickTip.
+ * Specifying 'persistent' in conjunction with 'helpTest' will create a ToolTip that remains visible, useful for embedding links in the text.
*/
afterRenderOrig : Ext.form.Field.prototype.afterRender,
afterRender : function() {
- var helpClass = null, wrapDiv = null, helpMark = null;
+ var helpClass = null, wrapDiv = null, helpMark = null, self = this;
if ( this.helpMarker === true )
{
wrapDiv = this.getEl().up('div');
@@ -79,12 +81,27 @@ Ext.override(Ext.form.Field, {
cls : helpClass
});
- Ext.QuickTips.register({
- target : helpMark,
- title : '',
- text : this.helpText,
- enabled : true
- });
+ // Use a ToolTip instead of QuickTip if we want the tip to remain in place.
+ // In this case the entire html fo the ToolTip will be set to the provided
+ // 'helpText'.
+ if(this.persistent)
+ {
+ var help = new Ext.ToolTip({
+ html : self.helpText,
+ anchor: 'left',
+ hideDelay: 10000
+ });
+ help.initTarget(helpMark);
+ }
+ else
+ {
+ Ext.QuickTips.register({
+ target : helpMark,
+ title : '',
+ text : this.helpText,
+ enabled : true
+ });
+ }
}
// original method
View
4 nexus-webapp/src/main/webapp/js/lib/string.js
@@ -35,7 +35,9 @@ Ext.applyIf(String.prototype, {
intIndexOfMatch = strText.indexOf(strTarget);
}
- return (strText);
+ // return new String, because returning "this" here without a #replace inbetween returns a char array,
+ // not a String (seen in chrome).
+ return String(strText);
}
});
View
2 nexus-webapp/src/main/webapp/js/repoServer.js
@@ -14,7 +14,7 @@
define('repoServer',
[
'Nexus/repository/AbstractRepoPanel',
- 'repoServer/FeedViewPanel',
+ 'Sonatype/repoServer/ArtifactContainer',
'repoServer/PrivilegeEditPanel',
'repoServer/RepoEditPanel',
'repoServer/RepoMaintPanel',
View
5 nexus-webapp/src/main/webapp/js/repoServer/RepoServer.js
@@ -288,11 +288,6 @@ define('repoServer/RepoServer',['extjs', 'sonatype', 'Sonatype/lib', 'Nexus/conf
title : 'Routing',
tabId : 'routes-config',
tabCode : Sonatype.repoServer.RoutesEditPanel
- }, {
- enabled : sp.checkPermission('nexus:feeds', sp.READ),
- title : 'System Feeds',
- tabId : 'feed-view-system-changes',
- tabCode : Sonatype.repoServer.FeedViewPanel
}]
});
View
3 nexus-webapp/src/main/webapp/style/Sonatype.css
@@ -196,7 +196,8 @@ button.st-icon-refresh {
}
.form-label-helpmark-combo {
- margin-left: 2px;
+ /* large margin needed to place helpmark next to combobox - the box is position:absolute, and helpmark was covered by dropdown arrow */
+ margin-left: 19px;
height: 16px;
width: 16px;
vertical-align: middle;
View
5 plugins/nexus-timeline-plugin/pom.xml
@@ -92,6 +92,11 @@
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
</plugin>
+
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>yuicompressor-maven-plugin</artifactId>
+ </plugin>
</plugins>
</build>
View
50 ...nexus-timeline-plugin/src/main/java/org/sonatype/nexus/rest/ui/TimelineUiContributor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rest.ui;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.sonatype.nexus.plugins.rest.UiContributionBuilder;
+import org.sonatype.nexus.plugins.rest.UiContributor;
+
+/**
+ * UI contribution for the timeline plugin.
+ *
+ * @since 2.5
+ */
+@Named
+@Singleton
+public class TimelineUiContributor
+ implements UiContributor
+{
+
+ public static final String ARTIFACT_ID = "nexus-timeline-plugin";
+
+ public static final String GROUP_ID = "org.sonatype.nexus.plugins";
+
+ private final UiContributionBuilder builder;
+
+ @Inject
+ public TimelineUiContributor()
+ {
+ this.builder = new UiContributionBuilder( this, GROUP_ID, ARTIFACT_ID );
+ }
+
+ @Override
+ public UiContribution contribute( final boolean debug )
+ {
+ return builder.build( debug );
+ }
+}
View
159 .../src/main/webapp/js/Nexus/ext/feedgrid.js → ...in/src/main/js/Nexus/timeline/FeedGrid.js
@@ -12,16 +12,21 @@
*/
// must pass in feedUrl that's local to our domain
// config: feedUrl required
-/*global define*/
+/*global NX, Ext*/
/**
- * FIXME This belongs to the timeline plugin and should be moved there.
+ * The main view for all feeds.
+ *
+ * @since 2.5
*/
-define('Nexus/ext/feedgrid', ['extjs'], function(Ext) {
- var ns = Ext.namespace('Nexus.ext');
- ns.FeedGrid = function(config) {
- Ext.apply(this, config);
+NX.define('Nexus.timeline.FeedGrid', {
+ extend : 'Ext.grid.GridPanel',
+
+ constructor : function(cfg) {
+ var self = this;
+
+ Ext.apply(self, cfg);
- this.store = new Ext.data.Store({
+ self.store = new Ext.data.Store({
// note: IE requires text/xml or application/xml to parse via XmlReader
// because it uses response.responseXML
proxy : new Ext.data.HttpProxy({
@@ -44,7 +49,7 @@ define('Nexus/ext/feedgrid', ['extjs'], function(Ext) {
autoLoad : false
});
- this.columns = [
+ self.columns = [
{
id : 'title',
header : "Title",
@@ -63,7 +68,7 @@ define('Nexus/ext/feedgrid', ['extjs'], function(Ext) {
}
];
- ns.FeedGrid.superclass.constructor.call(this, {
+ self.constructor.superclass.constructor.call(this, {
title : 'Select a feed from the list',
region : 'center',
id : 'topic-grid',
@@ -93,73 +98,91 @@ define('Nexus/ext/feedgrid', ['extjs'], function(Ext) {
}
]
});
+ },
- // this.on('rowcontextmenu', this.onContextClick, this);
- };
-
- Ext.extend(ns.FeedGrid, Ext.grid.GridPanel, {
-
- setFeed : function(name, url) {
- this.setTitle(name);
- this.feedUrl = url;
- this.store.proxy = new Ext.data.HttpProxy({
- url : this.feedUrl,
- headers : {
- 'accept' : 'text/xml'
- }
- });
- this.reloadFeed();
- },
-
- reloadFeed : function() {
- if (this.feedUrl) {
- this.store.reload();
+ // this.on('rowcontextmenu', this.onContextClick, this);
+ setFeed : function(name, url) {
+ this.setTitle(name);
+ this.feedUrl = url;
+ this.store.proxy = new Ext.data.HttpProxy({
+ url : this.feedUrl,
+ headers : {
+ 'accept' : 'text/xml'
}
- },
+ });
+ this.reloadFeed();
+ },
- togglePreview : function(show) {
- this.view.showPreview = show;
- this.view.refresh();
- },
+ /**
+ * Reload the feed store.
+ */
+ reloadFeed : function() {
+ if (this.feedUrl) {
+ this.store.reload();
+ }
+ },
- // within this function "this" is actually the GridView
- applyRowClass : function(record, rowIndex, p, ds) {
- if (this.showPreview) {
- var xf = Ext.util.Format;
- p.body = '<p>' + xf.ellipsis(xf.stripTags(record.data.description), 400) + '</p>';
- return 'x-grid3-row-expanded';
- }
- return 'x-grid3-row-collapsed';
- },
+ /**
+ * @private
+ */
+ togglePreview : function(show) {
+ this.view.showPreview = show;
+ this.view.refresh();
+ },
- formatDate : function(value, p, record, rowIndex, colIndex, store) {
- if (!value) {
- return '';
- }
- var now = new Date();
- var d = now.clearTime(true);
+ // within this function "this" is actually the GridView
+ applyRowClass : function(record, rowIndex, p, ds) {
+ if (this.showPreview) {
+ var xf = Ext.util.Format;
+ p.body = '<p>' + xf.ellipsis(xf.stripTags(record.data.description), 400) + '</p>';
+ return 'x-grid3-row-expanded';
+ }
+ return 'x-grid3-row-collapsed';
+ },
- // @todo: correct Ext's Date parsing of "GMT" timezone text, and text my
- // ISO8601 patches
- // @note: special handling for bad Ext.Date parsing
- value = value.add(Date.MINUTE, d.getTimezoneOffset() * (-1));
+ /**
+ * Date formatter that prefers "Today" or a day name over a real date.
+ *
+ * @private
+ *
+ * @param value The time to format.
+ * @returns {String} The formatted date.
+ */
+ formatDate : function(value) {
+ if (!value) {
+ return '';
+ }
+ var now = new Date();
+ var d = now.clearTime(true);
- var notime = value.clearTime(true).getTime();
- if (notime == d.getTime()) {
- return 'Today ' + value.dateFormat('g:i a');
- }
- d = d.add('d', -6);
- if (d.getTime() <= notime) {
- return value.dateFormat('D g:i a');
- }
- return value.dateFormat('n/j g:i a');
- },
+ // @todo: correct Ext's Date parsing of "GMT" timezone text, and text my
+ // ISO8601 patches
+ // @note: special handling for bad Ext.Date parsing
+ value = value.add(Date.MINUTE, d.getTimezoneOffset() * (-1));
- formatTitle : function(value, p, record, rowIndex, colIndex, store) {
- return String.format('<div class="topic"><b><a href="{1}" target="_blank">{0}</a></b><span class="author">{2}</span></div>',
- value, record.data.link, record.data.author);
+ var notime = value.clearTime(true).getTime();
+ if (notime == d.getTime()) {
+ return 'Today ' + value.dateFormat('g:i a');
+ }
+ d = d.add('d', -6);
+ if (d.getTime() <= notime) {
+ return value.dateFormat('D g:i a');
}
- });
+ return value.dateFormat('n/j g:i a');
+ },
- return ns.FeedGrid;
+ /**
+ * Converts a record to HTML.
+ *
+ * @private
+ *
+ * @param value The title of the entry.
+ * @param p
+ * @param record The record of the entry. Used to add a link and author description.
+ * @returns {String} HTML-formatted feed entry title.
+ */
+ formatTitle : function(value, p, record) {
+ return String.format('<div class="topic"><b><a href="{1}" target="_blank">{0}</a></b><span class="author">{2}</span></div>',
+ value, record.data.link, record.data.author);
+ }
});
View
99 ...ain/webapp/js/repoServer/FeedViewPanel.js → ...in/src/main/js/Nexus/timeline/FeedView.js
@@ -11,24 +11,28 @@
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
-/*global define*/
+/*global NX,Nexus,Ext,Sonatype*/
/**
- * FIXME This belongs to the timeline plugin and should be moved there.
+ * Inner view on selecting one feed.
+ *
+ * @since 2.5
*/
-define('repoServer/FeedViewPanel', ['extjs', 'Sonatype/all', 'Nexus/ext/feedgrid'], function(Ext, Sonatype, FeedGrid) {
-
- var ns = Ext.namespace('Sonatype.repoServer');
+NX.define('Nexus.timeline.FeedView', {
+ extend : 'Ext.Panel',
+ requires : ['Nexus.timeline.FeedGrid'],
/*
* config object: { feedUrl ; required title }
*/
- ns.FeedViewPanel = function(cfg) {
- Ext.apply(this, cfg || {}, {
+ constructor : function(cfg) {
+ var self = this;
+
+ Ext.apply(self, cfg || {}, {
feedUrl : '',
title : 'Feed Viewer'
});
- this.feedRecordConstructor = Ext.data.Record.create([
+ self.feedRecordConstructor = Ext.data.Record.create([
{
name : 'resourceURI'
},
@@ -38,22 +42,22 @@ define('repoServer/FeedViewPanel', ['extjs', 'Sonatype/all', 'Nexus/ext/feedgrid
}
]);
- this.feedReader = new Ext.data.JsonReader({
+ self.feedReader = new Ext.data.JsonReader({
root : 'data',
id : 'resourceURI'
- }, this.feedRecordConstructor);
+ }, self.feedRecordConstructor);
- this.feedsDataStore = new Ext.data.Store({
+ self.feedsDataStore = new Ext.data.Store({
url : Sonatype.config.repos.urls.feeds,
- reader : this.feedReader,
+ reader : self.feedReader,
sortInfo : {
field : 'name',
direction : 'ASC'
},
autoLoad : true
});
- this.feedsGridPanel = new Ext.grid.GridPanel({
+ self.feedsGridPanel = new Ext.grid.GridPanel({
id : 'st-feeds-grid',
region : 'north',
layout : 'fit',
@@ -73,28 +77,26 @@ define('repoServer/FeedViewPanel', ['extjs', 'Sonatype/all', 'Nexus/ext/feedgrid
text : 'Refresh',
icon : Sonatype.config.resourcePath + '/images/icons/arrow_refresh.png',
cls : 'x-btn-text-icon',
- scope : this,
handler : function() {
- this.feedsDataStore.reload();
- this.grid.reloadFeed();
+ self.feedsDataStore.reload();
+ self.grid.reloadFeed();
}
},
{
text : 'Subscribe',
icon : Sonatype.config.resourcePath + '/images/icons/feed.png',