You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2021/01/27 19:16:33 UTC

[archiva] branch master updated: Adding task admin implementation

This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git


The following commit(s) were added to refs/heads/master by this push:
     new b32d75a  Adding task admin implementation
b32d75a is described below

commit b32d75a85b4f5805d6d251ea2237f838087aa2bf
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Jan 27 20:16:26 2021 +0100

    Adding task admin implementation
---
 .../core/AbstractArtifactConsumerTest.java         |  15 +-
 .../repository/AbstractRepositoryPurgeTest.java    |  10 +-
 .../spring-context-cleanup-released-snapshots.xml  |   2 +-
 .../admin/model/admin/ArchivaAdministration.java   |   1 +
 .../model/admin/RepositoryTaskAdministration.java  | 115 +++++++
 .../archiva/admin/model/beans/IndexingTask.java    |  87 +++++
 .../admin/model/beans/MetadataScanTask.java        |  84 +++++
 .../admin/model/beans/RepositoryTaskInfo.java      | 121 +++++++
 .../archiva/admin/model/beans/ScanStatus.java      |  85 +++++
 .../admin/model/error/AdminErrors.properties       |   4 +
 .../archiva-repository-admin-default/pom.xml       |  17 +
 .../admin/DefaultRepositoryTaskAdministration.java | 360 +++++++++++++++++++++
 .../mock/MockRepositoryArchivaTaskScheduler.java   |   2 +-
 .../admin/RepositoryTaskAdministrationTest.java    | 267 +++++++++++++++
 .../src/test/resources/spring-context.xml          |   3 +-
 .../archiva/rest/api/model/v2/IndexingTask.java    |  12 +-
 .../archiva/rest/api/model/v2/Repository.java      |  12 +-
 .../archiva/rest/api/model/v2/ScanStatus.java      |  35 +-
 .../apache/archiva/rest/api/model/v2/ScanTask.java |   9 +-
 .../archiva/rest/api}/services/v2/ErrorKeys.java   |   2 +-
 .../rest/api/services/v2/RepositoryService.java    |   4 -
 .../v2/DefaultMavenManagedRepositoryService.java   |   1 +
 .../services/v2/DefaultRepositoryGroupService.java |   1 +
 .../rest/services/v2/DefaultRepositoryService.java | 112 +++----
 .../v2/DefaultSecurityConfigurationService.java    |   5 +-
 .../services/v2/NativeRepositoryServiceTest.java   | 162 ++++++++++
 26 files changed, 1420 insertions(+), 108 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java
index d6b116e..0ff779f 100644
--- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java
+++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java
@@ -25,8 +25,10 @@ import org.apache.archiva.configuration.FileType;
 import org.apache.archiva.configuration.FileTypes;
 import org.apache.archiva.consumers.KnownRepositoryContentConsumer;
 import org.apache.archiva.consumers.functors.ConsumerWantsFilePredicate;
+import org.apache.archiva.repository.base.ArchivaRepositoryRegistry;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.apache.commons.lang3.StringUtils;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,8 +39,7 @@ import javax.inject.Inject;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.*;
 
 @RunWith( ArchivaSpringJUnit4ClassRunner.class )
 @ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath:/spring-context.xml" } )
@@ -54,6 +55,8 @@ public abstract class AbstractArtifactConsumerTest
     @Inject
     ArchivaConfiguration archivaConfiguration;
 
+    ArchivaRepositoryRegistry repositoryRegistry;
+
 
     @Before
     public void setUp()
@@ -68,6 +71,14 @@ public abstract class AbstractArtifactConsumerTest
         archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("SHA256");
 
         repoLocation = Paths.get( "target/test-" + getName() + "/test-repo" );
+
+        repositoryRegistry = applicationContext.getBean( "repositoryRegistry", ArchivaRepositoryRegistry.class );
+        assertNotNull( repositoryRegistry );
+    }
+
+    @After
+    public void destroy() {
+        repositoryRegistry.destroy();
     }
 
 
diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurgeTest.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurgeTest.java
index cfcd19e..2f906a5 100644
--- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurgeTest.java
+++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurgeTest.java
@@ -24,6 +24,8 @@ import org.apache.archiva.metadata.repository.MetadataRepository;
 import org.apache.archiva.metadata.repository.RepositorySession;
 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
 import org.apache.archiva.repository.ManagedRepositoryContent;
+import org.apache.archiva.repository.RepositoryRegistry;
+import org.apache.archiva.repository.base.ArchivaRepositoryRegistry;
 import org.apache.archiva.repository.maven.metadata.storage.Maven2RepositoryPathTranslator;
 import org.apache.archiva.repository.base.BasicManagedRepository;
 import org.apache.archiva.repository.ReleaseScheme;
@@ -53,8 +55,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 import static org.mockito.Mockito.mock;
 
 /**
@@ -108,6 +109,8 @@ public abstract class AbstractRepositoryPurgeTest
 
     protected MetadataRepository metadataRepository;
 
+    protected ArchivaRepositoryRegistry repositoryRegistry;
+
     @Inject
     protected ApplicationContext applicationContext;
 
@@ -127,6 +130,8 @@ public abstract class AbstractRepositoryPurgeTest
         repositorySession = sessionControl.createMock( RepositorySession.class );
         metadataRepository = mock( MetadataRepository.class );
         sessionFactory = sessionFactoryControl.createMock( RepositorySessionFactory.class );
+        repositoryRegistry = applicationContext.getBean( "repositoryRegistry", ArchivaRepositoryRegistry.class );
+        assertNotNull( repositoryRegistry );
         EasyMock.expect( repositorySession.getRepository() ).andStubReturn( metadataRepository );
         EasyMock.expect( sessionFactory.createSession( ) ).andStubReturn( repositorySession );
 
@@ -138,6 +143,7 @@ public abstract class AbstractRepositoryPurgeTest
     {
         config = null;
         repo = null;
+        repositoryRegistry.destroy();
 
     }
 
diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/resources/spring-context-cleanup-released-snapshots.xml b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/resources/spring-context-cleanup-released-snapshots.xml
index 2f64658..ee3d8e0 100644
--- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/resources/spring-context-cleanup-released-snapshots.xml
+++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/resources/spring-context-cleanup-released-snapshots.xml
@@ -32,7 +32,7 @@
   </bean>
   <alias name="archivaConfiguration#cleanup-released-snapshots" alias="archivaConfiguration"/>
   <alias name="archivaConfiguration#cleanup-released-snapshots" alias="archivaConfiguration#default"/>
-  <context:component-scan base-package="org.apache.archiva.configuration,org.apache.archiva.repository.content.maven2,org.apache.archiva.indexer.maven"/>
+  <context:component-scan base-package="org.apache.archiva.configuration,org.apache.archiva.repository.content.base,org.apache.archiva.indexer.maven"/>
 
   <alias name="repositoryContentFactory#cleanup-released-snapshots" alias="repositoryContentFactory#default" />
 
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/ArchivaAdministration.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/ArchivaAdministration.java
index 7037f49..beda0b9 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/ArchivaAdministration.java
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/ArchivaAdministration.java
@@ -29,6 +29,7 @@ import org.apache.archiva.admin.model.beans.UiConfiguration;
 import java.util.List;
 
 /**
+ * Base administration interface for Archiva. Provides methods for managing archiva base tasks.
  * @author Olivier Lamy
  * @since 1.4-M1
  */
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/RepositoryTaskAdministration.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/RepositoryTaskAdministration.java
new file mode 100644
index 0000000..0375f2e
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/admin/RepositoryTaskAdministration.java
@@ -0,0 +1,115 @@
+package org.apache.archiva.admin.model.admin;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.admin.model.RepositoryAdminException;
+import org.apache.archiva.admin.model.beans.RepositoryTaskInfo;
+import org.apache.archiva.admin.model.beans.ScanStatus;
+import org.springframework.util.StopWatch;
+
+import java.util.List;
+
+/**
+ * Interface for managing repository scan tasks.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ * @since 3.0
+ */
+public interface RepositoryTaskAdministration
+{
+    /**
+     * Schedules a full repository scan for the given repository. Metadata and Index are updated.
+     * All files are scanned, even if they were not modified since the last scan.
+     *
+     * @param repositoryId the repository identifier
+     * @throws RepositoryAdminException if it was not possible to schedule the scan
+     */
+    void scheduleFullScan( String repositoryId ) throws RepositoryAdminException;
+
+    /**
+     * Schedules a scan that rebuilds the fulltext index of the repository.
+     *
+     * @param repositoryId the repository identifier
+     * @throws RepositoryAdminException if it was not possible to schedule the index scan
+     */
+    void scheduleIndexFullScan( String repositoryId ) throws RepositoryAdminException;
+
+    /**
+     * Schedules a scan that rebuilds the fulltext index of the repository.
+     *
+     * @param repositoryId the repository identifier
+     * @param relativePath the path to the file to add to the index
+     * @throws RepositoryAdminException if it was not possible to schedule the index scan
+     */
+    void scheduleIndexScan( String repositoryId, String relativePath ) throws RepositoryAdminException;
+
+    /**
+     * Schedules a scan that rebuilds metadata of the repository
+     *
+     * @param repositoryId the repository identifier
+     * @throws RepositoryAdminException if it was not possible to schedule the index scan
+     */
+    void scheduleMetadataFullScan( String repositoryId ) throws RepositoryAdminException;
+
+    /**
+     * Schedules a scan that rebuilds metadata of the repository but only for updated files.
+     *
+     * @param repositoryId the repository identifier
+     * @throws RepositoryAdminException if it was not possible to schedule the index scan
+     */
+    void scheduleMetadataUpdateScan( String repositoryId ) throws RepositoryAdminException;
+
+    /**
+     * Returns information about currently running scans for the given repository.
+     *
+     * @return the status information
+     * @param repositoryId the repository identifier
+     * @throws RepositoryAdminException if there was an error retrieving the scan status
+     */
+    ScanStatus getCurrentScanStatus(String repositoryId) throws RepositoryAdminException;
+
+
+    /**
+     * Returns information about currently running scans for all repositories.
+     *
+     * @return the status information
+     * @throws RepositoryAdminException if there was an error retrieving the scan status
+     */
+    ScanStatus getCurrentScanStatus() throws RepositoryAdminException;
+
+    /**
+     * Cancels the tasks either running or queued for the given repository.
+     * @param repositoryId the repository identifier
+     * @return a list of canceled tasks.
+     */
+    List<RepositoryTaskInfo> cancelTasks(String repositoryId) throws RepositoryAdminException;
+
+    /**
+     * Cancels the metadata scan tasks either running or queued for the given repository.
+     * @param repositoryId the repository identifier
+     * @return a list of canceled tasks.
+     */
+    List<RepositoryTaskInfo> cancelScanTasks(String repositoryId) throws RepositoryAdminException;
+
+    /**
+     * Cancels the indexing tasks either running or queued for the given repository.
+     * @param repositoryId the repository identifier
+     * @return a list of canceled tasks.
+     */
+    List<RepositoryTaskInfo> cancelIndexTasks(String repositoryId) throws RepositoryAdminException;
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/IndexingTask.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/IndexingTask.java
new file mode 100644
index 0000000..8b39c20
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/IndexingTask.java
@@ -0,0 +1,87 @@
+package org.apache.archiva.admin.model.beans;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Information about index update tasks running on a repository.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class IndexingTask extends RepositoryTaskInfo implements Serializable
+{
+    private static final long serialVersionUID = -1947200162602613310L;
+    /**
+     * <code>true</code>, if this task is just updating the existing index.
+     */
+    private boolean updateOnly;
+
+    public boolean isUpdateOnly( )
+    {
+        return updateOnly;
+    }
+
+    public void setUpdateOnly( boolean updateOnly )
+    {
+        this.updateOnly = updateOnly;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        IndexingTask that = (IndexingTask) o;
+
+        if ( isFullScan( ) != that.fullScan ) return false;
+        if ( updateOnly != that.updateOnly ) return false;
+        if ( isRunning( ) != that.isRunning( ) ) return false;
+        if ( getMaxExecutionTimeMs( ) != that.getMaxExecutionTimeMs( ) ) return false;
+        if ( !getRepositoryId( ).equals( that.getRepositoryId( ) ) ) return false;
+        return getResource( ).equals( that.getResource( ) );
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        int result = getRepositoryId( ).hashCode( );
+        result = 31 * result + ( isFullScan( ) ? 1 : 0 );
+        result = 31 * result + ( updateOnly ? 1 : 0 );
+        result = 31 * result + getResource( ).hashCode( );
+        result = 31 * result + ( isRunning( ) ? 1 : 0 );
+        result = 31 * result + (int) ( getMaxExecutionTimeMs( ) ^ ( getMaxExecutionTimeMs( ) >>> 32 ) );
+        return result;
+    }
+
+    @Override
+    public String toString( )
+    {
+        final StringBuilder sb = new StringBuilder( "IndexingTask{" );
+        sb.append( "repositoryId='" ).append( getRepositoryId( ) ).append( '\'' );
+        sb.append( ", fullRepository=" ).append( isFullScan( ) );
+        sb.append( ", updateOnly=" ).append( updateOnly );
+        sb.append( ", resource='" ).append( getResource( ) ).append( '\'' );
+        sb.append( ", running=" ).append( isRunning( ) );
+        sb.append( ", maxExecutionTimeMs=" ).append( getMaxExecutionTimeMs( ) );
+        sb.append( '}' );
+        return sb.toString( );
+    }
+
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/MetadataScanTask.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/MetadataScanTask.java
new file mode 100644
index 0000000..02ab7ee
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/MetadataScanTask.java
@@ -0,0 +1,84 @@
+package org.apache.archiva.admin.model.beans;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Serializable;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class MetadataScanTask extends RepositoryTaskInfo implements Serializable
+{
+    private static final long serialVersionUID = -681163357370848098L;
+    /**
+     * <code>true</code> if related artifacts are updated too.
+     */
+    private boolean updateRelatedArtifacts;
+
+    public boolean isUpdateRelatedArtifacts( )
+    {
+        return updateRelatedArtifacts;
+    }
+
+    public void setUpdateRelatedArtifacts( boolean updateRelatedArtifacts )
+    {
+        this.updateRelatedArtifacts = updateRelatedArtifacts;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        MetadataScanTask scanTask = (MetadataScanTask) o;
+
+        if ( updateRelatedArtifacts != scanTask.updateRelatedArtifacts ) return false;
+        if ( fullScan != scanTask.fullScan ) return false;
+        if ( running != scanTask.running ) return false;
+        if ( maxExecutionTimeMs != scanTask.maxExecutionTimeMs ) return false;
+        if ( !repositoryId.equals( scanTask.repositoryId ) ) return false;
+        return resource.equals( scanTask.resource );
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        int result = repositoryId.hashCode( );
+        result = 31 * result + ( updateRelatedArtifacts ? 1 : 0 );
+        result = 31 * result + ( fullScan ? 1 : 0 );
+        result = 31 * result + ( running ? 1 : 0 );
+        result = 31 * result + resource.hashCode( );
+        result = 31 * result + (int) ( maxExecutionTimeMs ^ ( maxExecutionTimeMs >>> 32 ) );
+        return result;
+    }
+
+    @Override
+    public String toString( )
+    {
+        final StringBuilder sb = new StringBuilder( "ScanTask{" );
+        sb.append( "repositoryId='" ).append( repositoryId ).append( '\'' );
+        sb.append( ", updateRelatedArtifacts=" ).append( updateRelatedArtifacts );
+        sb.append( ", fullRepository=" ).append( fullScan );
+        sb.append( ", running=" ).append( running );
+        sb.append( ", resource='" ).append( resource ).append( '\'' );
+        sb.append( ", maxExecutionTimeMs=" ).append( maxExecutionTimeMs );
+        sb.append( '}' );
+        return sb.toString( );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/RepositoryTaskInfo.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/RepositoryTaskInfo.java
new file mode 100644
index 0000000..9a0a838
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/RepositoryTaskInfo.java
@@ -0,0 +1,121 @@
+package org.apache.archiva.admin.model.beans;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.time.OffsetDateTime;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class RepositoryTaskInfo
+{
+    /**
+     * The repository identifier
+     */
+    protected String repositoryId = "";
+    /**
+     * <code>true</code>, if all files are scanned. <code>false</code>, if only updated files are scanned
+     */
+    protected boolean fullScan;
+    /**
+     * The scanned resource, if this is not a full repository scan
+     */
+    protected String resource = "";
+    /**
+     * Running status of the task.
+     */
+    protected boolean running = false;
+    /**
+     * The maximum execution time set for this task.
+     */
+    protected long maxExecutionTimeMs = 0;
+    /**
+     * The time when the status check was built
+     */
+    OffsetDateTime checkTime;
+
+    public RepositoryTaskInfo( )
+    {
+        this.checkTime = OffsetDateTime.now( );
+    }
+
+    public RepositoryTaskInfo( OffsetDateTime checkTime )
+    {
+        this.checkTime = checkTime;
+    }
+
+    public String getRepositoryId( )
+    {
+        return repositoryId;
+    }
+
+    public void setRepositoryId( String repositoryId )
+    {
+        this.repositoryId = repositoryId==null?"":repositoryId;
+    }
+
+    public boolean isFullScan( )
+    {
+        return fullScan;
+    }
+
+    public void setFullScan( boolean fullScan )
+    {
+        this.fullScan = fullScan;
+    }
+
+    public String getResource( )
+    {
+        return resource;
+    }
+
+    public void setResource( String resource )
+    {
+        this.resource = resource == null ? "" : resource;
+    }
+
+    public boolean isRunning( )
+    {
+        return running;
+    }
+
+    public void setRunning( boolean running )
+    {
+        this.running = running;
+    }
+
+    public long getMaxExecutionTimeMs( )
+    {
+        return maxExecutionTimeMs;
+    }
+
+    public void setMaxExecutionTimeMs( long maxExecutionTimeMs )
+    {
+        this.maxExecutionTimeMs = maxExecutionTimeMs;
+    }
+
+    public OffsetDateTime getCheckTime( )
+    {
+        return checkTime;
+    }
+
+    public void setCheckTime( OffsetDateTime checkTime )
+    {
+        this.checkTime = checkTime;
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/ScanStatus.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/ScanStatus.java
new file mode 100644
index 0000000..50dc46e
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/java/org/apache/archiva/admin/model/beans/ScanStatus.java
@@ -0,0 +1,85 @@
+package org.apache.archiva.admin.model.beans;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about running and queued repository scans.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class ScanStatus implements Serializable
+{
+    private List<IndexingTask> indexingQueue = new ArrayList<>(  );
+    private List<MetadataScanTask> scanQueue = new ArrayList<>(  );
+
+    public ScanStatus( )
+    {
+    }
+
+    public boolean isMetadataScanRunning( )
+    {
+        return scanQueue.size( ) > 0 && scanQueue.get(0).isRunning();
+    }
+
+    public int getMetadataScanQueueSize() {
+        int size= scanQueue.size( );
+        if (size>0) {
+            return size-1;
+        } else {
+            return 0;
+        }
+    }
+
+    public boolean isIndexScanRunning( )
+    {
+        return indexingQueue.size()>0 && indexingQueue.get(0).isRunning();
+    }
+
+    public int getIndexScanQueueSize() {
+        int size = indexingQueue.size();
+        if (size>0) {
+            return size-1;
+        } else {
+            return 0;
+        }
+    }
+
+    public List<IndexingTask> getIndexingQueue( )
+    {
+        return indexingQueue;
+    }
+
+    public void setIndexingQueue( List<IndexingTask> indexingQueue )
+    {
+        this.indexingQueue = new ArrayList<>( indexingQueue );
+    }
+
+    public List<MetadataScanTask> getScanQueue( )
+    {
+        return scanQueue;
+    }
+
+    public void setScanQueue( List<MetadataScanTask> scanQueue )
+    {
+        this.scanQueue = new ArrayList<>( scanQueue );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/resources/org/apache/archiva/admin/model/error/AdminErrors.properties b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/resources/org/apache/archiva/admin/model/error/AdminErrors.properties
index 4addc0a..82056a0 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/resources/org/apache/archiva/admin/model/error/AdminErrors.properties
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-api/src/main/resources/org/apache/archiva/admin/model/error/AdminErrors.properties
@@ -26,3 +26,7 @@ repository_group.repository.not_found=The member repository with id "{0}" does n
 repository_group.registry.add_error=The registry could not add the repository "{0}": {1}
 repository_group.registry.update_error=The registry could not update the repository "{0}": {1}
 repository_group.not_editable=The repository group "{0}" is not editable
+repository.id.invalid=The given repository id {0} is not valid. 
+repository.not_found=The repository with the given id "{0}" was not found
+repository.file.not_found=No file "{1}" found in the repository "{0}"
+repository.scan.task_retrieval_failed=Could not get scan task information: {0}
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/pom.xml b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/pom.xml
index 80e4620..2f5f277 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/pom.xml
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/pom.xml
@@ -117,6 +117,18 @@
       <groupId>org.apache.archiva.components</groupId>
       <artifactId>archiva-components-spring-taskqueue</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.archiva</groupId>
+      <artifactId>archiva-scheduler-indexing</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.archiva</groupId>
+      <artifactId>archiva-scheduler-repository</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.archiva.maven</groupId>
+      <artifactId>archiva-maven-scheduler</artifactId>
+    </dependency>
 
 
     <dependency>
@@ -167,6 +179,11 @@
 
     <!-- Test scope -->
     <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-proxy</artifactId>
       <scope>test</scope>
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/main/java/org/apache/archiva/admin/repository/admin/DefaultRepositoryTaskAdministration.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/main/java/org/apache/archiva/admin/repository/admin/DefaultRepositoryTaskAdministration.java
new file mode 100644
index 0000000..5dc9bb1
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/main/java/org/apache/archiva/admin/repository/admin/DefaultRepositoryTaskAdministration.java
@@ -0,0 +1,360 @@
+package org.apache.archiva.admin.repository.admin;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.admin.model.RepositoryAdminException;
+import org.apache.archiva.admin.model.admin.RepositoryTaskAdministration;
+import org.apache.archiva.admin.model.beans.IndexingTask;
+import org.apache.archiva.admin.model.beans.MetadataScanTask;
+import org.apache.archiva.admin.model.beans.RepositoryTaskInfo;
+import org.apache.archiva.admin.model.beans.ScanStatus;
+import org.apache.archiva.components.taskqueue.TaskQueueException;
+import org.apache.archiva.components.taskqueue.execution.TaskQueueExecutor;
+import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsManager;
+import org.apache.archiva.repository.RepositoryRegistry;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
+import org.apache.archiva.scheduler.indexing.IndexingArchivaTaskScheduler;
+import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
+import org.apache.archiva.scheduler.repository.model.RepositoryTask;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Named;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Service( "repositoryTaskAdministration#default" )
+public class DefaultRepositoryTaskAdministration implements RepositoryTaskAdministration
+{
+    private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryTaskAdministration.class );
+
+
+    final RepositoryRegistry repositoryRegistry;
+
+    final
+    TaskQueueExecutor<ArtifactIndexingTask> indexingTaskExecutor;
+
+    final
+    TaskQueueExecutor<RepositoryTask> scanningTaskExecutor;
+
+    private final RepositoryArchivaTaskScheduler repositoryArchivaTaskScheduler;
+
+    private final IndexingArchivaTaskScheduler indexingArchivaTaskScheduler;
+
+    public DefaultRepositoryTaskAdministration( RepositoryRegistry repositoryRegistry,
+                                     @Named( value = "taskQueueExecutor#indexing" ) TaskQueueExecutor<ArtifactIndexingTask> indexingTaskExecutor,
+                                     @Named( value = "taskQueueExecutor#repository-scanning" ) TaskQueueExecutor<RepositoryTask> scanningTaskExecutor,
+                                     @Named( value = "archivaTaskScheduler#repository" ) RepositoryArchivaTaskScheduler repositoryArchivaTaskScheduler,
+                                     @Named( value = "archivaTaskScheduler#indexing" ) IndexingArchivaTaskScheduler indexingArchivaTaskScheduler)
+    {
+        this.repositoryRegistry = repositoryRegistry;
+        this.indexingTaskExecutor = indexingTaskExecutor;
+        this.scanningTaskExecutor = scanningTaskExecutor;
+        this.repositoryArchivaTaskScheduler = repositoryArchivaTaskScheduler;
+        this.indexingArchivaTaskScheduler = indexingArchivaTaskScheduler;
+    }
+
+    @Override
+    public void scheduleFullScan( String repositoryId ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        try
+        {
+            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+            if (repository==null) {
+                throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+            }
+            ArtifactIndexingTask task =
+                new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, repository.getIndexingContext( ) );
+            task.setExecuteOnEntireRepo( true );
+            task.setOnlyUpdate( false );
+            indexingArchivaTaskScheduler.queueTask( task );
+            repositoryArchivaTaskScheduler.queueTask( new RepositoryTask( repositoryId, true ) );
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not queue the task: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_queue_error", e, e.getMessage( ) );
+        }
+    }
+
+    @Override
+    public void scheduleIndexFullScan( String repositoryId ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        try
+        {
+            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+            if (repository==null) {
+                throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+            }
+            ArtifactIndexingTask task =
+                new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, repository.getIndexingContext( ) );
+            task.setExecuteOnEntireRepo( true );
+            task.setOnlyUpdate( false );
+            indexingArchivaTaskScheduler.queueTask( task );
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not queue the task: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_queue_error", e, e.getMessage( ) );
+        }
+    }
+
+    @Override
+    public void scheduleIndexScan( String repositoryId, String relativePath ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        try
+        {
+            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+            if (repository==null) {
+                throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+            }
+            StorageAsset asset = repository.getAsset( relativePath );
+            if (!asset.exists()) {
+                throw RepositoryAdminException.ofKey( "repository.file.not_found", repositoryId, relativePath );
+            }
+            ArtifactIndexingTask task =
+                new ArtifactIndexingTask( repository, asset.getFilePath( ), ArtifactIndexingTask.Action.FINISH, repository.getIndexingContext( ) );
+            task.setExecuteOnEntireRepo( false );
+            task.setOnlyUpdate( true );
+            indexingArchivaTaskScheduler.queueTask( task );
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not queue the task: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_queue_error", e, e.getMessage( ) );
+        }
+
+    }
+
+    @Override
+    public void scheduleMetadataFullScan( String repositoryId ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        try
+        {
+            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+            if (repository==null) {
+                throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+            }
+            repositoryArchivaTaskScheduler.queueTask( new RepositoryTask( repositoryId, true ) );
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not queue the task: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_queue_error", e, e.getMessage( ) );
+        }
+
+    }
+
+    @Override
+    public void scheduleMetadataUpdateScan( String repositoryId ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        try
+        {
+            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+            if (repository==null) {
+                throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+            }
+            repositoryArchivaTaskScheduler.queueTask( new RepositoryTask( repositoryId, false ) );
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not queue the task: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_queue_error", e, e.getMessage( ) );
+        }
+        
+    }
+
+    public static MetadataScanTask getMetadataScanTaskInfo(RepositoryTask repositoryTask) {
+        MetadataScanTask scanTask = new MetadataScanTask( );
+        scanTask.setFullScan( repositoryTask.isScanAll());
+        scanTask.setUpdateRelatedArtifacts( repositoryTask.isUpdateRelatedArtifacts() );
+        StorageAsset file = repositoryTask.getResourceFile( );
+        scanTask.setResource( repositoryTask.getResourceFile( )==null?"":repositoryTask.getResourceFile().toString( ) );
+        scanTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTime() );
+        scanTask.setRepositoryId( repositoryTask.getRepositoryId( ) );
+        return scanTask;
+    }
+
+    public static IndexingTask getIndexingTaskInfo(ArtifactIndexingTask repositoryTask) {
+        IndexingTask indexingTask = new IndexingTask( );
+        indexingTask.setFullScan( repositoryTask.isExecuteOnEntireRepo());
+        indexingTask.setUpdateOnly( repositoryTask.isOnlyUpdate() );
+        indexingTask.setResource( repositoryTask.getResourceFile( )==null?"":repositoryTask.getResourceFile().toString( ) );
+        indexingTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTime() );
+        indexingTask.setRepositoryId( repositoryTask.getRepository().getId() );
+        return indexingTask;
+
+    }
+
+    public void updateScanInfo( ScanStatus scanStatus, RepositoryTask runningRepositoryTask, List<RepositoryTask> taskQueue) {
+        List<MetadataScanTask> newScanQueue = new ArrayList<>( );
+        if (runningRepositoryTask!=null) {
+            MetadataScanTask taskInfo = getMetadataScanTaskInfo( runningRepositoryTask );
+            taskInfo.setRunning( true );
+            newScanQueue.add( 0,  taskInfo);
+        }
+        newScanQueue.addAll( taskQueue.stream( ).map( task -> getMetadataScanTaskInfo( task ) ).collect( Collectors.toList( ) ) );
+        scanStatus.setScanQueue( newScanQueue );
+    }
+
+    public void updateIndexInfo( ScanStatus scanStatus, ArtifactIndexingTask runningIndexingTask, List<ArtifactIndexingTask> taskQueue) {
+        List<IndexingTask> newIndexQueue = new ArrayList<>(  );
+        if (runningIndexingTask!=null) {
+            IndexingTask taskInfo = getIndexingTaskInfo( runningIndexingTask );
+            taskInfo.setRunning( true );
+            newIndexQueue.add(taskInfo );
+        }
+        newIndexQueue.addAll( taskQueue.stream( ).map( task -> getIndexingTaskInfo( task ) ).collect( Collectors.toList( ) ) );
+        scanStatus.setIndexingQueue( newIndexQueue );
+    }
+
+
+    @Override
+    public ScanStatus getCurrentScanStatus( String repositoryId ) throws RepositoryAdminException
+    {
+        if ( StringUtils.isEmpty( repositoryId ) ) {
+            throw RepositoryAdminException.ofKey( "repository.id.invalid", "" );
+        }
+        org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
+        if (repository==null) {
+            throw RepositoryAdminException.ofKey( "repository.not_found", repositoryId );
+        }
+        ScanStatus status = new ScanStatus( );
+        try
+        {
+            RepositoryTask scanTask = scanningTaskExecutor.getCurrentTask( );
+            if ( scanTask!=null && !repositoryId.equals( scanTask.getRepositoryId( ) ) )
+            {
+                scanTask = null;
+            }
+            ArtifactIndexingTask indexTask = indexingTaskExecutor.getCurrentTask( );
+            if ( indexTask!=null && !repositoryId.equals( indexTask.getRepository( ).getId( ) ) )
+            {
+                indexTask = null;
+            }
+            updateScanInfo( status, scanTask, scanningTaskExecutor.getQueue( ).getQueueSnapshot( ).stream( ).filter( task -> repositoryId.equals( task.getRepositoryId( ) ) ).collect( Collectors.toList( ) ) );
+            updateIndexInfo( status, indexTask, indexingTaskExecutor.getQueue( ).getQueueSnapshot( ).stream( ).filter( task -> repositoryId.equals( task.getRepository( ).getId( ) ) ).collect( Collectors.toList( ) ) );
+            return status;
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not get task information: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_retrieval_failed", e.getMessage( ) );
+        }
+
+    }
+
+    @Override
+    public ScanStatus getCurrentScanStatus( ) throws RepositoryAdminException
+    {
+        ScanStatus status = new ScanStatus( );
+        try
+        {
+            RepositoryTask scanTask = scanningTaskExecutor.getCurrentTask( );
+            ArtifactIndexingTask indexTask = indexingTaskExecutor.getCurrentTask( );
+            updateScanInfo( status, scanTask, scanningTaskExecutor.getQueue( ).getQueueSnapshot() );
+            updateIndexInfo( status, indexTask, indexingTaskExecutor.getQueue( ).getQueueSnapshot( ) );
+            return status;
+        }
+        catch ( TaskQueueException e )
+        {
+            log.error( "Could not get task information: {}", e.getMessage( ), e );
+            throw RepositoryAdminException.ofKey( "repository.scan.task_retrieval_failed", e.getMessage( ) );
+        }
+
+    }
+
+    @Override
+    public List<RepositoryTaskInfo> cancelTasks( String repositoryId ) throws RepositoryAdminException
+    {
+        ArrayList<RepositoryTaskInfo> resultList = new ArrayList<>( );
+        resultList.addAll( cancelScanTasks( repositoryId ) );
+        resultList.addAll( cancelIndexTasks( repositoryId ) );
+        return resultList;
+    }
+
+    @Override
+    public List<RepositoryTaskInfo> cancelScanTasks( String repositoryId ) throws RepositoryAdminException
+    {
+        try
+        {
+            ArrayList<RepositoryTaskInfo> resultList = new ArrayList<>( );
+            List<RepositoryTask> removeTasks = scanningTaskExecutor.getQueue( ).getQueueSnapshot( ).stream( ).filter( task -> repositoryId.equals( task.getRepositoryId() ) ).collect( Collectors.toList( ) );
+            scanningTaskExecutor.getQueue( ).removeAll( removeTasks );
+            RepositoryTask currentTask = scanningTaskExecutor.getCurrentTask( );
+            if ( currentTask != null && repositoryId.equals( currentTask.getRepositoryId()) )
+            {
+                scanningTaskExecutor.cancelTask( currentTask );
+                resultList.add( getMetadataScanTaskInfo( currentTask ) );
+            }
+            resultList.addAll( removeTasks.stream( ).map( task -> getMetadataScanTaskInfo( task ) ).collect( Collectors.toList( ) ) );
+            return resultList;
+        }
+        catch ( TaskQueueException e )
+        {
+            throw RepositoryAdminException.ofKey( "repository.task.dequeue_failed", repositoryId );
+        }
+    }
+
+    @Override
+    public List<RepositoryTaskInfo> cancelIndexTasks( String repositoryId ) throws RepositoryAdminException
+    {
+        try
+        {
+            ArrayList<RepositoryTaskInfo> resultList = new ArrayList<>( );
+            List<ArtifactIndexingTask> removeTasks = indexingTaskExecutor.getQueue( ).getQueueSnapshot( ).stream( ).filter( task -> repositoryId.equals( task.getRepository( ).getId( ) ) ).collect( Collectors.toList( ) );
+            indexingTaskExecutor.getQueue( ).removeAll( removeTasks );
+            ArtifactIndexingTask currentTask = indexingTaskExecutor.getCurrentTask( );
+            if ( currentTask != null && repositoryId.equals( currentTask.getRepository( ).getId( ) ) )
+            {
+                indexingTaskExecutor.cancelTask( currentTask );
+                resultList.add( getIndexingTaskInfo( currentTask ) );
+            }
+            resultList.addAll( removeTasks.stream( ).map( task -> getIndexingTaskInfo( task ) ).collect( Collectors.toList( ) ) );
+            return resultList;
+        }
+        catch ( TaskQueueException e )
+        {
+            throw RepositoryAdminException.ofKey( "repository.task.dequeue_failed", repositoryId );
+        }
+    }
+
+
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MockRepositoryArchivaTaskScheduler.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MockRepositoryArchivaTaskScheduler.java
index 8c3cfe2..1540b46 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MockRepositoryArchivaTaskScheduler.java
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MockRepositoryArchivaTaskScheduler.java
@@ -26,7 +26,7 @@ import org.springframework.stereotype.Service;
 /**
  * @author Olivier Lamy
  */
-@Service("archivaTaskScheduler#repository")
+@Service("archivaTaskScheduler#mock")
 public class MockRepositoryArchivaTaskScheduler
     implements RepositoryArchivaTaskScheduler
 {
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/repository/admin/RepositoryTaskAdministrationTest.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/repository/admin/RepositoryTaskAdministrationTest.java
new file mode 100644
index 0000000..6cdf31a
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/repository/admin/RepositoryTaskAdministrationTest.java
@@ -0,0 +1,267 @@
+package org.apache.archiva.admin.repository.admin;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.admin.model.RepositoryAdminException;
+import org.apache.archiva.admin.model.admin.RepositoryTaskAdministration;
+import org.apache.archiva.admin.model.beans.ScanStatus;
+import org.apache.archiva.components.taskqueue.TaskQueue;
+import org.apache.archiva.components.taskqueue.TaskQueueException;
+import org.apache.archiva.components.taskqueue.execution.TaskQueueExecutor;
+import org.apache.archiva.repository.ManagedRepository;
+import org.apache.archiva.repository.RepositoryRegistry;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
+import org.apache.archiva.scheduler.indexing.IndexingArchivaTaskScheduler;
+import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
+import org.apache.archiva.scheduler.repository.model.RepositoryTask;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Tag( "archiva-admin" )
+@DisplayName( "Unit Tests for RepositoryTaskAdministration" )
+public class RepositoryTaskAdministrationTest
+{
+
+    private RepositoryTaskAdministration taskAdministration;
+    private TaskQueueExecutor<ArtifactIndexingTask> indexingTaskExecutor;
+    private TaskQueueExecutor<RepositoryTask> scanningTaskExecutor;
+    private RepositoryArchivaTaskScheduler repositoryArchivaTaskScheduler;
+    private IndexingArchivaTaskScheduler indexingArchivaTaskScheduler;
+    private RepositoryRegistry registry;
+
+    @BeforeEach
+    public void init() {
+        registry = mock( RepositoryRegistry.class );
+        indexingTaskExecutor = mock( TaskQueueExecutor.class );
+        scanningTaskExecutor = mock( TaskQueueExecutor.class );
+        repositoryArchivaTaskScheduler = mock( RepositoryArchivaTaskScheduler.class );
+        indexingArchivaTaskScheduler = mock( IndexingArchivaTaskScheduler.class );
+        this.taskAdministration = new DefaultRepositoryTaskAdministration( registry, indexingTaskExecutor,
+            scanningTaskExecutor, repositoryArchivaTaskScheduler, indexingArchivaTaskScheduler );
+    }
+
+    @Test
+    public void testScanStatus() throws RepositoryAdminException, TaskQueueException
+    {
+        TaskQueue queue = mock( TaskQueue.class );
+        TaskQueue indexQueue = mock( TaskQueue.class );
+
+        List<RepositoryTask> scanList = new ArrayList<>( );
+        RepositoryTask scanTask1 = new RepositoryTask( );
+        scanTask1.setRepositoryId( "abcde" );
+        scanTask1.setScanAll( true );
+        scanTask1.setResourceFile( null );
+        scanList.add( scanTask1 );
+        RepositoryTask scanTask2 = new RepositoryTask( );
+        scanTask2.setRepositoryId( "testrepo2" );
+        scanTask2.setScanAll( true );
+        scanTask2.setResourceFile( null );
+        scanList.add( scanTask1 );
+
+        List<ArtifactIndexingTask> indexList = new ArrayList<>( );
+        ArtifactIndexingTask indexTask1 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask1.getRepository( ).getId( ) ).thenReturn( "indexrepo1" );
+        when( indexTask1.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask1.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask1 );
+        ArtifactIndexingTask indexTask2 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask2.getRepository( ).getId( ) ).thenReturn( "indexrepo2" );
+        when( indexTask2.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask2.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask2 );
+
+        when( scanningTaskExecutor.getQueue( ) ).thenReturn( queue );
+        when( indexingTaskExecutor.getQueue( ) ).thenReturn( indexQueue );
+        when( queue.getQueueSnapshot( ) ).thenReturn( scanList );
+        when( indexQueue.getQueueSnapshot( ) ).thenReturn( indexList );
+        ScanStatus currentScanStatus = taskAdministration.getCurrentScanStatus( );
+        assertNotNull( currentScanStatus );
+        assertNotNull( currentScanStatus.getIndexingQueue( ) );
+        assertNotNull( currentScanStatus.getScanQueue( ) );
+        assertEquals( 2, currentScanStatus.getScanQueue( ).size( ) );
+        assertEquals( 2, currentScanStatus.getIndexingQueue( ).size( ) );
+    }
+
+    @Test
+    public void testScanStatusWithId() throws RepositoryAdminException, TaskQueueException
+    {
+        TaskQueue queue = mock( TaskQueue.class );
+        TaskQueue indexQueue = mock( TaskQueue.class );
+
+        List<RepositoryTask> scanList = new ArrayList<>( );
+        RepositoryTask scanTask1 = new RepositoryTask( );
+        scanTask1.setRepositoryId( "abcde" );
+        scanTask1.setScanAll( true );
+        scanTask1.setResourceFile( null );
+        scanList.add( scanTask1 );
+        RepositoryTask scanTask2 = new RepositoryTask( );
+        scanTask2.setRepositoryId( "testrepo2" );
+        scanTask2.setScanAll( true );
+        scanTask2.setResourceFile( null );
+        scanList.add( scanTask1 );
+
+        List<ArtifactIndexingTask> indexList = new ArrayList<>( );
+        ArtifactIndexingTask indexTask1 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask1.getRepository( ).getId( ) ).thenReturn( "indexrepo1" );
+        when( indexTask1.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask1.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask1 );
+        ArtifactIndexingTask indexTask2 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask2.getRepository( ).getId( ) ).thenReturn( "indexrepo2" );
+        when( indexTask2.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask2.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask2 );
+
+        when( scanningTaskExecutor.getQueue( ) ).thenReturn( queue );
+        when( indexingTaskExecutor.getQueue( ) ).thenReturn( indexQueue );
+        when( queue.getQueueSnapshot( ) ).thenReturn( scanList );
+        when( indexQueue.getQueueSnapshot( ) ).thenReturn( indexList );
+        when( registry.getManagedRepository( "indexrepo2" ) ).thenReturn( mock( ManagedRepository.class ) );
+        ScanStatus currentScanStatus = taskAdministration.getCurrentScanStatus( "indexrepo2");
+        assertNotNull( currentScanStatus );
+        assertNotNull( currentScanStatus.getIndexingQueue( ) );
+        assertNotNull( currentScanStatus.getScanQueue( ) );
+        assertEquals( 0, currentScanStatus.getScanQueue( ).size( ) );
+        assertEquals( 1, currentScanStatus.getIndexingQueue( ).size( ) );
+    }
+
+    @Test
+    public void testScheduleFullScan() throws RepositoryAdminException, TaskQueueException
+    {
+        when( registry.getManagedRepository( "internal" ) ).thenReturn( mock( ManagedRepository.class ) );
+        taskAdministration.scheduleFullScan( "internal" );
+        verify( repositoryArchivaTaskScheduler, times(1) ).queueTask( any() );
+        verify( indexingArchivaTaskScheduler, times(1) ).queueTask( any() );
+    }
+
+    @Test
+    public void testScheduleIndexScan() throws RepositoryAdminException, TaskQueueException
+    {
+        when( registry.getManagedRepository( "internal" ) ).thenReturn( mock( ManagedRepository.class ) );
+        taskAdministration.scheduleIndexFullScan( "internal" );
+        ArgumentCaptor<ArtifactIndexingTask> captor = ArgumentCaptor.forClass( ArtifactIndexingTask.class );
+        verify( repositoryArchivaTaskScheduler, times(0) ).queueTask( any() );
+        verify( indexingArchivaTaskScheduler, times(1) ).queueTask( captor.capture() );
+        assertTrue(captor.getValue().isExecuteOnEntireRepo());
+    }
+
+    @Test
+    public void testScheduleIndexScanWithFile() throws RepositoryAdminException, TaskQueueException
+    {
+        ManagedRepository managedRepo = mock( ManagedRepository.class, RETURNS_DEEP_STUBS );
+        when( registry.getManagedRepository( "internal" ) ).thenReturn( managedRepo );
+        StorageAsset asset = mock( StorageAsset.class );
+        when( asset.getFilePath( ) ).thenReturn( Paths.get( "abc/def/ghij.pom" ) );
+        when( asset.exists( ) ).thenReturn( true );
+        when( registry.getManagedRepository( "internal" ).getAsset( "abc/def/ghij.pom" ) ).thenReturn( asset );
+        taskAdministration.scheduleIndexScan( "internal", "abc/def/ghij.pom" );
+        ArgumentCaptor<ArtifactIndexingTask> captor = ArgumentCaptor.forClass( ArtifactIndexingTask.class );
+        verify( repositoryArchivaTaskScheduler, times(0) ).queueTask( any() );
+        verify( indexingArchivaTaskScheduler, times(1) ).queueTask( captor.capture() );
+        ArtifactIndexingTask caption = captor.getValue( );
+        assertFalse(caption.isExecuteOnEntireRepo());
+        assertEquals( "abc/def/ghij.pom", caption.getResourceFile( ).toString() );
+    }
+
+    @Test
+    public void testScheduleMetadataScan() throws RepositoryAdminException, TaskQueueException
+    {
+        when( registry.getManagedRepository( "internal" ) ).thenReturn( mock( ManagedRepository.class ) );
+        taskAdministration.scheduleMetadataFullScan( "internal" );
+        ArgumentCaptor<RepositoryTask> captor = ArgumentCaptor.forClass( RepositoryTask.class );
+        verify( repositoryArchivaTaskScheduler, times(1) ).queueTask( captor.capture( ) );
+        verify( indexingArchivaTaskScheduler, times(0) ).queueTask( any() );
+        assertTrue(captor.getValue().isScanAll());
+    }
+
+    @Test
+    public void testScheduleMetadataUpdateScan() throws RepositoryAdminException, TaskQueueException
+    {
+        when( registry.getManagedRepository( "internal" ) ).thenReturn( mock( ManagedRepository.class ) );
+        taskAdministration.scheduleMetadataUpdateScan( "internal" );
+        ArgumentCaptor<RepositoryTask> captor = ArgumentCaptor.forClass( RepositoryTask.class );
+        verify( repositoryArchivaTaskScheduler, times(1) ).queueTask( captor.capture( ) );
+        verify( indexingArchivaTaskScheduler, times(0) ).queueTask( any() );
+        assertFalse(captor.getValue().isScanAll());
+    }
+
+
+    @Test
+    void cancelAllTasks() throws TaskQueueException, RepositoryAdminException
+    {
+        TaskQueue queue = mock( TaskQueue.class );
+        TaskQueue indexQueue = mock( TaskQueue.class );
+
+        List<RepositoryTask> scanList = new ArrayList<>( );
+        RepositoryTask scanTask1 = new RepositoryTask( );
+        scanTask1.setRepositoryId( "abcde" );
+        scanTask1.setScanAll( true );
+        scanTask1.setResourceFile( null );
+        scanList.add( scanTask1 );
+        RepositoryTask scanTask2 = new RepositoryTask( );
+        scanTask2.setRepositoryId( "testrepo2" );
+        scanTask2.setScanAll( true );
+        scanTask2.setResourceFile( null );
+        scanList.add( scanTask1 );
+
+        List<ArtifactIndexingTask> indexList = new ArrayList<>( );
+        ArtifactIndexingTask indexTask1 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask1.getRepository( ).getId( ) ).thenReturn( "indexrepo1" );
+        when( indexTask1.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask1.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask1 );
+        ArtifactIndexingTask indexTask2 = mock( ArtifactIndexingTask.class, RETURNS_DEEP_STUBS );
+        when( indexTask2.getRepository( ).getId( ) ).thenReturn( "indexrepo2" );
+        when( indexTask2.isExecuteOnEntireRepo( ) ).thenReturn( true );
+        when( indexTask2.getResourceFile( ) ).thenReturn( null );
+        indexList.add( indexTask2 );
+
+        when( scanningTaskExecutor.getQueue( ) ).thenReturn( queue );
+        when( indexingTaskExecutor.getQueue( ) ).thenReturn( indexQueue );
+        when( queue.getQueueSnapshot( ) ).thenReturn( scanList );
+        when( indexQueue.getQueueSnapshot( ) ).thenReturn( indexList );
+        taskAdministration.cancelTasks( "indexrepo1" );
+        ArgumentCaptor<List> scanCaptor = ArgumentCaptor.forClass( List.class );
+        ArgumentCaptor<List> indexCaptor = ArgumentCaptor.forClass( List.class );
+
+        verify( queue ).removeAll( scanCaptor.capture() );
+        verify( indexQueue ).removeAll( indexCaptor.capture() );
+
+        List scanCancelList = scanCaptor.getValue( );
+        List indexCancelList = indexCaptor.getValue( );
+        assertEquals( 0, scanCancelList.size( ) );
+        assertEquals( 1, indexCancelList.size( ) );
+        assertEquals( "indexrepo1", ( (ArtifactIndexingTask) indexCancelList.get( 0 ) ).getRepository( ).getId( ) );
+
+
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/resources/spring-context.xml b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/resources/spring-context.xml
index 5c201e5..203c326 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/resources/spring-context.xml
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/resources/spring-context.xml
@@ -31,8 +31,9 @@
        default-lazy-init="true">
 
   <context:annotation-config/>
-  <context:component-scan base-package="org.apache.archiva.admin.mock,org.apache.archiva.repository.content.maven2"/>
+  <context:component-scan base-package="org.apache.archiva.admin.mock,org.apache.archiva.repository.content.base"/>
 
+  <alias name="archivaTaskScheduler#mock" alias="archivaTaskScheduler#default" />
   <bean name="scheduler" class="org.apache.archiva.components.scheduler.DefaultScheduler">
     <property name="properties">
       <props>
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/IndexingTask.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/IndexingTask.java
index e4d6684..b3bc509 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/IndexingTask.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/IndexingTask.java
@@ -37,13 +37,13 @@ public class IndexingTask implements Serializable
     private boolean running = false;
     private long maxExecutionTimeMs = 0;
 
-    public static IndexingTask of( ArtifactIndexingTask repositoryTask ) {
+    public static IndexingTask of( org.apache.archiva.admin.model.beans.IndexingTask repositoryTask ) {
         IndexingTask indexingTask = new IndexingTask( );
-        indexingTask.setFullRepository( repositoryTask.isExecuteOnEntireRepo());
-        indexingTask.setUpdateOnly( repositoryTask.isOnlyUpdate() );
-        indexingTask.setResource( repositoryTask.getResourceFile( ).toString( ) );
-        indexingTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTime() );
-        indexingTask.setRepositoryId( repositoryTask.getRepository().getId() );
+        indexingTask.setFullRepository( repositoryTask.isFullScan());
+        indexingTask.setUpdateOnly( repositoryTask.isUpdateOnly() );
+        indexingTask.setResource( repositoryTask.getResource() );
+        indexingTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTimeMs() );
+        indexingTask.setRepositoryId( repositoryTask.getRepositoryId() );
         return indexingTask;
     }
 
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/Repository.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/Repository.java
index ea4993d..04f77b9 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/Repository.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/Repository.java
@@ -97,10 +97,18 @@ public class Repository implements Serializable
         } else {
             myLocale = locale;
         }
+        String repoName = repository.getName( myLocale );
+        if (repoName==null) {
+            repoName = repository.getName( );
+        }
+        String description = repository.getDescription( myLocale );
+        if (description==null)  {
+            description = repository.getDescription( );
+        }
         Repository newRepo = new Repository( );
         newRepo.setId( repository.getId() );
-        newRepo.setName( repository.getName( myLocale ) );
-        newRepo.setDescription( repository.getDescription( myLocale ) );
+        newRepo.setName( repoName );
+        newRepo.setDescription( description );
         newRepo.setLocation( repository.getLocation().toASCIIString() );
         newRepo.setIndex( repository.hasIndex() );
         newRepo.setLayout( repository.getLayout() );
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanStatus.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanStatus.java
index 9800bbb..27c6b02 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanStatus.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanStatus.java
@@ -18,6 +18,7 @@ package org.apache.archiva.rest.api.model.v2;
  */
 
 import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.archiva.admin.model.beans.MetadataScanTask;
 import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
 
@@ -43,30 +44,18 @@ public class ScanStatus implements Serializable
     {
     }
 
-    public void updateScanInfo( RepositoryTask runningRepositoryTask, List<RepositoryTask> taskQueue) {
-        List<ScanTask> newScanQueue = new ArrayList<>( );
-        if (runningRepositoryTask==null) {
-            this.scanRunning=false;
-        } else {
-            this.scanRunning=true;
-            newScanQueue.add( 0, ScanTask.of( runningRepositoryTask ) );
-        }
-        newScanQueue.addAll( taskQueue.stream( ).map( task -> ScanTask.of( task ) ).collect( Collectors.toList( ) ) );
-        this.scanQueued = taskQueue.size( );
-        this.scanQueue = newScanQueue;
-    }
+    public static ScanStatus of( org.apache.archiva.admin.model.beans.ScanStatus modelStatus ) {
+        ScanStatus status = new ScanStatus( );
+        status.setIndexRunning( modelStatus.isIndexScanRunning() );
+        status.setScanRunning( modelStatus.isMetadataScanRunning() );
+        List<org.apache.archiva.admin.model.beans.IndexingTask> indexQueue = modelStatus.getIndexingQueue( );
+        status.setIndexingQueue( indexQueue.stream().map(IndexingTask::of).collect( Collectors.toList()) );
+        status.setIndexQueued( indexQueue.size( ) > 0 ? indexQueue.size( ) - 1 : 0 );
+        List<MetadataScanTask> scanQueue = modelStatus.getScanQueue( );
+        status.setScanQueue( scanQueue.stream().map( ScanTask::of ).collect( Collectors.toList()) );
+        status.setScanQueued( scanQueue.size( ) > 0 ? scanQueue.size( ) - 1 : 0 );
+        return status;
 
-    public void updateIndexInfo( ArtifactIndexingTask runningIndexingTask, List<ArtifactIndexingTask> taskQueue) {
-        List<IndexingTask> newIndexQueue = new ArrayList<>(  );
-        if (runningIndexingTask==null) {
-            this.indexRunning=false;
-        } else {
-            this.indexRunning=true;
-            newIndexQueue.add(IndexingTask.of( runningIndexingTask ) );
-        }
-        newIndexQueue.addAll( taskQueue.stream( ).map( task -> IndexingTask.of( task ) ).collect( Collectors.toList( ) ) );
-        this.indexQueued = taskQueue.size( );
-        this.indexingQueue = newIndexQueue;
     }
 
     @Schema( name = "scan_running", description = "True, if a scan is currently running" )
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanTask.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanTask.java
index 96aa96f..1dc1530 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanTask.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/ScanTask.java
@@ -18,6 +18,7 @@ package org.apache.archiva.rest.api.model.v2;
  */
 
 import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.archiva.admin.model.beans.MetadataScanTask;
 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
 
 import java.io.Serializable;
@@ -36,12 +37,12 @@ public class ScanTask implements Serializable
     private String resource = "";
     private long maxExecutionTimeMs = 0;
 
-    public static ScanTask of( RepositoryTask repositoryTask ) {
+    public static ScanTask of( MetadataScanTask repositoryTask ) {
         ScanTask scanTask = new ScanTask( );
-        scanTask.setFullRepository( repositoryTask.isScanAll());
+        scanTask.setFullRepository( repositoryTask.isFullScan());
         scanTask.setUpdateRelatedArtifacts( repositoryTask.isUpdateRelatedArtifacts() );
-        scanTask.setResource( repositoryTask.getResourceFile( ).toString( ) );
-        scanTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTime() );
+        scanTask.setResource( repositoryTask.getResource() );
+        scanTask.setMaxExecutionTimeMs( repositoryTask.getMaxExecutionTimeMs() );
         scanTask.setRepositoryId( repositoryTask.getRepositoryId( ) );
         return scanTask;
     }
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorKeys.java
similarity index 98%
rename from archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java
rename to archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorKeys.java
index 5544ff3..c328a48 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorKeys.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.rest.services.v2;/*
+package org.apache.archiva.rest.api.services.v2;/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/RepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/RepositoryService.java
index 8092ac7..4063977 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/RepositoryService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/RepositoryService.java
@@ -144,10 +144,6 @@ public interface RepositoryService
         throws ArchivaRestServiceException;
 
 
-    /**
-     * scan directories
-     * @since 1.4-M3
-     */
     @Path ("managed/{id}/scan/now")
     @POST
     @Produces ({ APPLICATION_JSON })
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java
index 3c9c3a6..e380a24 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java
@@ -24,6 +24,7 @@ import org.apache.archiva.components.rest.util.QueryHelper;
 import org.apache.archiva.rest.api.model.v2.FileInfo;
 import org.apache.archiva.rest.api.model.v2.MavenManagedRepository;
 import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException;
+import org.apache.archiva.rest.api.services.v2.ErrorKeys;
 import org.apache.archiva.rest.api.services.v2.ErrorMessage;
 import org.apache.archiva.rest.api.services.v2.MavenManagedRepositoryService;
 import org.slf4j.Logger;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryGroupService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryGroupService.java
index 42b7193..9290873 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryGroupService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryGroupService.java
@@ -46,6 +46,7 @@ import org.apache.archiva.redback.rest.services.RedbackRequestInformation;
 import org.apache.archiva.redback.users.User;
 import org.apache.archiva.rest.api.model.v2.RepositoryGroup;
 import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException;
+import org.apache.archiva.rest.api.services.v2.ErrorKeys;
 import org.apache.archiva.rest.api.services.v2.ErrorMessage;
 import org.apache.archiva.rest.api.services.v2.RepositoryGroupService;
 import org.apache.commons.lang3.StringUtils;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryService.java
index 4f34cd1..7e41b09 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultRepositoryService.java
@@ -17,11 +17,10 @@ package org.apache.archiva.rest.services.v2;
  * under the License.
  */
 
+import org.apache.archiva.admin.model.RepositoryAdminException;
+import org.apache.archiva.admin.model.admin.RepositoryTaskAdministration;
 import org.apache.archiva.components.rest.model.PagedResult;
 import org.apache.archiva.components.rest.util.QueryHelper;
-import org.apache.archiva.components.taskqueue.Task;
-import org.apache.archiva.components.taskqueue.TaskQueueException;
-import org.apache.archiva.components.taskqueue.execution.TaskQueueExecutor;
 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
 import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsManager;
 import org.apache.archiva.repository.RepositoryRegistry;
@@ -33,19 +32,14 @@ import org.apache.archiva.rest.api.model.v2.Repository;
 import org.apache.archiva.rest.api.model.v2.RepositoryStatistics;
 import org.apache.archiva.rest.api.model.v2.ScanStatus;
 import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException;
+import org.apache.archiva.rest.api.services.v2.ErrorKeys;
 import org.apache.archiva.rest.api.services.v2.ErrorMessage;
 import org.apache.archiva.rest.api.services.v2.RepositoryService;
-import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
-import org.apache.archiva.scheduler.indexing.IndexingArchivaTaskScheduler;
-import org.apache.archiva.scheduler.indexing.maven.ArchivaIndexingTaskExecutor;
-import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
-import org.apache.archiva.scheduler.repository.model.RepositoryTask;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.core.Response;
 import java.util.Comparator;
@@ -58,49 +52,64 @@ import java.util.stream.Collectors;
  * @author Martin Stockhammer <ma...@apache.org>
  * @since 3.0
  */
-@Service("v2.repositoryService#rest")
+@Service( "v2.repositoryService#rest" )
 public class DefaultRepositoryService implements RepositoryService
 {
 
-    @Inject
+    final
     RepositoryRegistry repositoryRegistry;
 
-    @Inject
+    final
     RepositoryStatisticsManager repositoryStatisticsManager;
 
-    @Inject
-    @Named(value="taskQueueExecutor#indexing")
-    TaskQueueExecutor<ArtifactIndexingTask> indexingTaskExecutor;
+    private final RepositoryTaskAdministration repositoryTaskAdministration;
 
-    @Inject
-    @Named(value="taskQueueExecutor#repository-scanning")
-    TaskQueueExecutor<RepositoryTask> scanningTaskExecutor;
-
-    @Inject
-    @Named(value = "archivaTaskScheduler#repository")
-    private RepositoryArchivaTaskScheduler repositoryArchivaTaskScheduler;
-
-    @Inject
-    @Named( value = "archivaTaskScheduler#indexing" )
-    private IndexingArchivaTaskScheduler indexingArchivaTaskScheduler;
-
-    @Inject
-    private RepositoryScanner repoScanner;
+    private final RepositoryScanner repoScanner;
 
 
     private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryService.class );
     private static final QueryHelper<org.apache.archiva.repository.Repository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} );
+
     static
     {
         QUERY_HELPER.addStringFilter( "id", org.apache.archiva.repository.Repository::getId );
         QUERY_HELPER.addStringFilter( "name", org.apache.archiva.repository.Repository::getName );
+        QUERY_HELPER.addStringFilter( "description", org.apache.archiva.repository.Repository::getDescription );
+        QUERY_HELPER.addStringFilter( "type", repo -> repo.getType( ).name( ) );
+        QUERY_HELPER.addBooleanFilter( "scanned", org.apache.archiva.repository.Repository::isScanned );
         QUERY_HELPER.addNullsafeFieldComparator( "id", org.apache.archiva.repository.Repository::getId );
         QUERY_HELPER.addNullsafeFieldComparator( "name", org.apache.archiva.repository.Repository::getName );
+        QUERY_HELPER.addNullsafeFieldComparator( "type", repo -> repo.getType( ).name( ) );
+        QUERY_HELPER.addNullsafeFieldComparator( "boolean", org.apache.archiva.repository.Repository::isScanned );
+    }
+
+    public DefaultRepositoryService( RepositoryRegistry repositoryRegistry, RepositoryStatisticsManager repositoryStatisticsManager,
+                                     @Named( value = "repositoryTaskAdministration#default") RepositoryTaskAdministration repositoryTaskAdministration,
+                                     RepositoryScanner repoScanner )
+    {
+        this.repositoryRegistry = repositoryRegistry;
+        this.repositoryStatisticsManager = repositoryStatisticsManager;
+        this.repoScanner = repoScanner;
+        this.repositoryTaskAdministration = repositoryTaskAdministration;
+    }
+
+    private void handleAdminException( RepositoryAdminException e ) throws ArchivaRestServiceException
+    {
+        log.error( "Repository admin error: {}", e.getMessage( ), e );
+        if ( e.keyExists( ) )
+        {
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PREFIX + e.getKey( ), e.getParameters( ) ) );
+        }
+        else
+        {
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ADMIN_ERROR, e.getMessage( ) ) );
+        }
     }
 
+
     @Override
     public PagedResult<Repository> getRepositories( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order,
-                                                    String localeString) throws ArchivaRestServiceException
+                                                    String localeString ) throws ArchivaRestServiceException
     {
         final Locale locale = StringUtils.isNotEmpty( localeString ) ? Locale.forLanguageTag( localeString ) : Locale.getDefault( );
         boolean isAscending = QUERY_HELPER.isAscending( order );
@@ -123,7 +132,8 @@ public class DefaultRepositoryService implements RepositoryService
     @Override
     public RepositoryStatistics getManagedRepositoryStatistics( String repositoryId ) throws ArchivaRestServiceException
     {
-        if (repositoryRegistry.getManagedRepository( repositoryId )==null) {
+        if ( repositoryRegistry.getManagedRepository( repositoryId ) == null )
+        {
             throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_MANAGED_NOT_FOUND, repositoryId ), 404 );
         }
         try
@@ -142,17 +152,13 @@ public class DefaultRepositoryService implements RepositoryService
     {
         try
         {
-            org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository( repositoryId );
-            ArtifactIndexingTask task =
-                new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, repository.getIndexingContext() );
-            task.setExecuteOnEntireRepo( true );
-            task.setOnlyUpdate( !fullScan );
-            indexingArchivaTaskScheduler.queueTask( task );
-            repositoryArchivaTaskScheduler.queueTask( new RepositoryTask( repositoryId, fullScan ) );
+            repositoryTaskAdministration.scheduleFullScan( repositoryId );
             return Response.ok( ).build( );
-        }  catch ( TaskQueueException e ) {
-            log.error( "Could not queue the task: {}", e.getMessage( ), e );
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.TASK_QUEUE_FAILED, e.getMessage( ) ) );
+        }
+        catch ( RepositoryAdminException e )
+        {
+            handleAdminException( e );
+            return Response.serverError( ).build( );
         }
     }
 
@@ -166,34 +172,22 @@ public class DefaultRepositoryService implements RepositoryService
         }
         catch ( RepositoryScannerException e )
         {
-            log.error( e.getMessage(), e );
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_SCAN_FAILED, e.getMessage() ));
+            log.error( e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_SCAN_FAILED, e.getMessage( ) ) );
         }
     }
 
     @Override
     public ScanStatus getScanStatus( String repositoryId ) throws ArchivaRestServiceException
     {
-        ScanStatus status = new ScanStatus( );
         try
         {
-            RepositoryTask scanTask = scanningTaskExecutor.getCurrentTask( );
-            if ( !repositoryId.equals( scanTask.getRepositoryId( ) ) )
-            {
-                scanTask=null;
-            }
-            ArtifactIndexingTask indexTask = indexingTaskExecutor.getCurrentTask( );
-            if (!repositoryId.equals(indexTask.getRepository().getId())) {
-                indexTask = null;
-            }
-            status.updateScanInfo( scanTask, scanningTaskExecutor.getQueue( ).getQueueSnapshot( ).stream( ).filter( task -> repositoryId.equals(task.getRepositoryId()) ).collect( Collectors.toList() ) );
-            status.updateIndexInfo( indexTask, indexingTaskExecutor.getQueue( ).getQueueSnapshot( ).stream().filter( task -> repositoryId.equals(task.getRepository().getId())).collect( Collectors.toList()) );
-            return status;
+            return ScanStatus.of( repositoryTaskAdministration.getCurrentScanStatus( ) );
         }
-        catch ( TaskQueueException e )
+        catch ( RepositoryAdminException e )
         {
-            log.error( "Could not get task information: {}", e.getMessage( ), e );
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.TASK_QUEUE_FAILED, e.getMessage( ) ) );
+            handleAdminException( e );
+            return null;
         }
     }
 
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java
index 9902074..360d03e 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java
@@ -56,6 +56,7 @@ import org.apache.archiva.rest.api.model.v2.CacheConfiguration;
 import org.apache.archiva.rest.api.model.v2.LdapConfiguration;
 import org.apache.archiva.rest.api.model.v2.SecurityConfiguration;
 import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException;
+import org.apache.archiva.rest.api.services.v2.ErrorKeys;
 import org.apache.archiva.rest.api.services.v2.ErrorMessage;
 import org.apache.archiva.rest.api.services.v2.SecurityConfigurationService;
 import org.apache.commons.collections4.CollectionUtils;
@@ -87,8 +88,8 @@ import java.util.ResourceBundle;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import static org.apache.archiva.rest.services.v2.ErrorKeys.INVALID_RESULT_SET_ERROR;
-import static org.apache.archiva.rest.services.v2.ErrorKeys.REPOSITORY_ADMIN_ERROR;
+import static org.apache.archiva.rest.api.services.v2.ErrorKeys.INVALID_RESULT_SET_ERROR;
+import static org.apache.archiva.rest.api.services.v2.ErrorKeys.REPOSITORY_ADMIN_ERROR;
 
 /**
  * @author Martin Stockhammer <ma...@apache.org>
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeRepositoryServiceTest.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeRepositoryServiceTest.java
new file mode 100644
index 0000000..0c74d3a
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeRepositoryServiceTest.java
@@ -0,0 +1,162 @@
+package org.apache.archiva.rest.services.v2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.restassured.response.Response;
+import org.apache.archiva.components.rest.model.PagedResult;
+import org.apache.archiva.rest.api.model.v2.Repository;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import java.util.List;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.http.ContentType.JSON;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@TestInstance( TestInstance.Lifecycle.PER_CLASS )
+@Tag( "rest-native" )
+@TestMethodOrder( MethodOrderer.Random.class )
+@DisplayName( "Native REST tests for V2 RepositoryService" )
+public class NativeRepositoryServiceTest extends AbstractNativeRestServices
+{
+    @Override
+    protected String getServicePath( )
+    {
+        return "/repositories";
+    }
+
+    @BeforeAll
+    void setup( ) throws Exception
+    {
+        super.setupNative( );
+    }
+
+    @AfterAll
+    void destroy( ) throws Exception
+    {
+        super.shutdownNative( );
+    }
+
+    @Test
+    void testGetRepositories() {
+        String token = getAdminToken( );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .get( "" )
+                .prettyPeek()
+                .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+        PagedResult<Repository> repositoryPagedResult = response.getBody( ).jsonPath( ).getObject( "", PagedResult.class );
+        assertEquals( 3, repositoryPagedResult.getPagination( ).getTotalCount( ) );
+        List<Repository> data = response.getBody( ).jsonPath( ).getList( "data", Repository.class );
+        assertTrue( data.stream( ).anyMatch( p -> "central".equals( p.getId( ) ) ) );
+        assertTrue( data.stream( ).anyMatch( p -> "internal".equals( p.getId( ) ) ) );
+        assertTrue( data.stream( ).anyMatch( p -> "snapshots".equals( p.getId( ) ) ) );
+        Repository snapshotRepo = data.stream( ).filter( p -> "snapshots".equals( p.getId( ) ) ).findFirst( ).get( );
+        assertEquals( "Archiva Managed Snapshot Repository", snapshotRepo.getName( ) );
+        assertEquals( "MAVEN", snapshotRepo.getType() );
+        assertEquals( "managed", snapshotRepo.getCharacteristic() );
+        assertEquals( "default", snapshotRepo.getLayout() );
+        assertTrue( snapshotRepo.isScanned( ) );
+        assertTrue( snapshotRepo.isIndex( ) );
+
+        Repository centralRepo = data.stream( ).filter( p -> "central".equals( p.getId( ) ) ).findFirst( ).get( );
+        assertEquals( "Central Repository", centralRepo.getName( ) );
+        assertEquals( "MAVEN", centralRepo.getType() );
+        assertEquals( "remote", centralRepo.getCharacteristic() );
+        assertEquals( "default", centralRepo.getLayout() );
+
+
+    }
+
+    @Test
+    void testGetFilteredRepositories() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .queryParam( "q", "central" )
+            .get( "" )
+            .prettyPeek()
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+        PagedResult<Repository> repositoryPagedResult = response.getBody( ).jsonPath( ).getObject( "", PagedResult.class );
+        assertEquals( 1, repositoryPagedResult.getPagination( ).getTotalCount( ) );
+        List<Repository> data = response.getBody( ).jsonPath( ).getList( "data", Repository.class );
+        assertTrue( data.stream( ).anyMatch( p -> "central".equals( p.getId( ) ) ) );
+    }
+
+
+    @Test
+    void getStatistics() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .get( "managed/internal/statistics" )
+            .prettyPeek()
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+
+    }
+
+    @Test
+    void scheduleScan() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .post( "managed/internal/scan/schedule" )
+            .prettyPeek()
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+
+    }
+
+    @Test
+    void immediateScan() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .post( "managed/internal/scan/now" )
+            .prettyPeek()
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+
+    }
+
+    @Test
+    void scanStatus() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .get( "managed/internal/scan/status" )
+            .prettyPeek()
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+
+    }
+}