]> source.dussan.org Git - archiva.git/commitdiff
Changing content item API
authorMartin Stockhammer <martin_s@apache.org>
Sun, 24 May 2020 16:18:37 +0000 (18:18 +0200)
committerMartin Stockhammer <martin_s@apache.org>
Sat, 30 May 2020 18:00:22 +0000 (20:00 +0200)
21 files changed:
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/GenericManagedRepositoryContent.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ItemConversionException.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/ContentItem.java
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/DataItem.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaArtifact.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaContentItem.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaDataItem.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaNamespace.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaProject.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaVersion.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/BaseContentItem.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ContentItemBuilder.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ArchivaContentItemOptBuilder.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/DataItemOptBuilder.java
archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java
archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java

diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/GenericManagedRepositoryContent.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/GenericManagedRepositoryContent.java
new file mode 100644 (file)
index 0000000..7242f5d
--- /dev/null
@@ -0,0 +1,141 @@
+package org.apache.archiva.repository;
+
+/*
+ * 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.repository.content.ContentItem;
+import org.apache.archiva.repository.content.ItemNotFoundException;
+import org.apache.archiva.repository.content.ItemSelector;
+import org.apache.archiva.repository.storage.StorageAsset;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public interface GenericManagedRepositoryContent
+{
+    /**
+     * Delete all items that match the given selector. The type and number of deleted items
+     * depend on the specific selector:
+     * <ul>
+     *     <li>namespace: the complete namespace is deleted (recursively if the recurse flag is set)</li>
+     *     <li>project: the complete project and all contained versions are deleted</li>
+     *     <li>version: the version inside the project is deleted (project is required)</li>
+     *     <li>artifactId: all artifacts that match the id (project and version are required)</li>
+     *     <li>artifactVersion: all artifacts that match the version (project and version are required)</li>
+     *     <li></li>
+     * </ul>
+     *
+     * @param selector the item selector that selects the artifacts to delete
+     * @param consumer a consumer of the items that will be called after deletion
+     * @returns the list of items that are deleted
+     * @throws ContentAccessException if the deletion was not possible or only partly successful, because the access
+     * to the artifacts failed
+     * @throws IllegalArgumentException if the selector does not specify valid artifacts to delete
+     */
+    void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException;
+
+    /**
+     * Removes the specified content item and if the item is a container or directory,
+     * all content stored under the given item.
+     *
+     * @param item the item.
+     * @throws ItemNotFoundException if the item cannot be found
+     * @throws ContentAccessException if the deletion was not possible or only partly successful, because the access
+     *  to the artifacts failed
+     */
+    void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException;
+
+    /**
+     * Returns a item for the given selector. The type of the returned item depends on the
+     * selector.
+     *
+     * @param selector the item selector
+     * @return the content item that matches the given selector
+     * @throws ContentAccessException if an error occured while accessing the backend
+     * @throws IllegalArgumentException if the selector does not select a valid content item
+     */
+    ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException;
+
+    /**
+     * Returns a stream of items that match the given selector. It may return a stream of mixed types,
+     * like namespaces, projects, versions and artifacts. It will not select a specific type.
+     * The selector can specify the '*' pattern for all fields.
+     * The returned elements will be provided by depth first.
+     *
+     * @param selector the item selector that specifies the items
+     * @return the stream of content items
+     * @throws ContentAccessException if the access to the underlying storage failed
+     * @throws IllegalArgumentException if a illegal coordinate combination was provided
+     */
+    Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException;
+
+    /**
+     * Returns the item that matches the given path. The item at the path must not exist.
+     *
+     * @param path the path string that points to the item
+     * @return the content item if the path is a valid item path
+     * @throws LayoutException if the path is not valid for the repository layout
+     */
+    ContentItem toItem( String path ) throws LayoutException;
+
+    /**
+     * Returns the item that matches the given asset path. The asset must not exist.
+     *
+     * @param assetPath the path to the artifact or directory
+     * @return the item, if it is a valid path for the repository layout
+     * @throws LayoutException if the path is not valid for the repository
+     */
+    ContentItem toItem( StorageAsset assetPath ) throws LayoutException;
+
+    /**
+     * Returns true, if the selector coordinates point to a existing item in the repository.
+     *
+     * @param selector the item selector
+     * @return <code>true</code>, if there exists such a item, otherwise <code>false</code>
+     */
+    boolean hasContent( ItemSelector selector );
+
+    /**
+     * Returns the parent of the item.
+     * @param item the current item
+     * @return the parent item, or <code>null</code> if no such item exists
+     */
+    ContentItem getParent(ContentItem item);
+
+    /**
+     * Returns the list of children items.
+     * @param item the current item
+     * @return the list of children, or a empty list, if no children exist
+     */
+    List<? extends ContentItem> getChildren( ContentItem item);
+
+    /**
+     * Tries to apply the given characteristic to the content item. If the layout does not allow this,
+     * it will throw a <code>LayoutException</code>.
+     *
+     * @param clazz the characteristic class to apply
+     * @param item the content item
+     * @param <T> The characteristic
+     * @return the applied characteristic
+     */
+    <T extends ContentItem> T applyCharacteristic(Class<T> clazz, ContentItem item) throws LayoutException;
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ItemConversionException.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ItemConversionException.java
new file mode 100644 (file)
index 0000000..2290111
--- /dev/null
@@ -0,0 +1,49 @@
+package org.apache.archiva.repository;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class ItemConversionException extends RuntimeException
+{
+    public ItemConversionException( )
+    {
+    }
+
+    public ItemConversionException( String message )
+    {
+        super( message );
+    }
+
+    public ItemConversionException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public ItemConversionException( Throwable cause )
+    {
+        super( cause );
+    }
+
+    public ItemConversionException( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace )
+    {
+        super( message, cause, enableSuppression, writableStackTrace );
+    }
+}
index a093c8a6d2abcfe749ba21b7720f3538a4849367..9b2b318b95f55683146ce0c069b45a2b6a6346e9 100644 (file)
@@ -45,54 +45,11 @@ import java.util.stream.Stream;
  *
  * This interface
  */
-public interface ManagedRepositoryContent extends RepositoryContent
+public interface ManagedRepositoryContent extends RepositoryContent, GenericManagedRepositoryContent
 {
 
     /// *****************   New generation interface **********************
 
-    /**
-     * Delete all items that match the given selector. The type and number of deleted items
-     * depend on the specific selector:
-     * <ul>
-     *     <li>namespace: the complete namespace is deleted (recursively if the recurse flag is set)</li>
-     *     <li>project: the complete project and all contained versions are deleted</li>
-     *     <li>version: the version inside the project is deleted (project is required)</li>
-     *     <li>artifactId: all artifacts that match the id (project and version are required)</li>
-     *     <li>artifactVersion: all artifacts that match the version (project and version are required)</li>
-     *     <li></li>
-     * </ul>
-     *
-     * @param selector the item selector that selects the artifacts to delete
-     * @param consumer a consumer of the items that will be called after deletion
-     * @returns the list of items that are deleted
-     * @throws ContentAccessException if the deletion was not possible or only partly successful, because the access
-     * to the artifacts failed
-     * @throws IllegalArgumentException if the selector does not specify valid artifacts to delete
-     */
-    void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer) throws ContentAccessException, IllegalArgumentException;
-
-    /**
-     * Removes the specified content item and if the item is a container or directory,
-     * all content stored under the given item.
-     *
-     * @param item the item.
-     * @throws ItemNotFoundException if the item cannot be found
-     * @throws ContentAccessException if the deletion was not possible or only partly successful, because the access
-     *  to the artifacts failed
-     */
-    void deleteItem( ContentItem item) throws ItemNotFoundException, ContentAccessException;
-
-
-    /**
-     * Returns a item for the given selector. The type of the returned item depends on the
-     * selector.
-     *
-     * @param selector the item selector
-     * @return the content item that matches the given selector
-     * @throws ContentAccessException if an error occured while accessing the backend
-     * @throws IllegalArgumentException if the selector does not select a valid content item
-     */
-    ContentItem getItem(ItemSelector selector) throws ContentAccessException, IllegalArgumentException;
 
     /**
      * Returns the namespace for the given selected coordinates. The selector must specify a namespace. All other
@@ -211,19 +168,6 @@ public interface ManagedRepositoryContent extends RepositoryContent
      */
     Stream<? extends Artifact> newArtifactStream( ItemSelector selector) throws ContentAccessException;
 
-    /**
-     * Returns a stream of items that match the given selector. It may return a stream of mixed types,
-     * like namespaces, projects, versions and artifacts. It will not select a specific type.
-     * The selector can specify the '*' pattern for all fields.
-     * The returned elements will be provided by depth first.
-     *
-     * @param selector the item selector that specifies the items
-     * @return the stream of content items
-     * @throws ContentAccessException if the access to the underlying storage failed
-     * @throws IllegalArgumentException if a illegal coordinate combination was provided
-     */
-    Stream<? extends ContentItem> newItemStream(ItemSelector selector, boolean parallel) throws ContentAccessException, IllegalArgumentException;
-
 
     /**
      * Return the projects that are part of the given namespace.
@@ -264,6 +208,16 @@ public interface ManagedRepositoryContent extends RepositoryContent
      */
     List<? extends Version> getVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException;
 
+    /**
+     * Returns all found artifact versions that can be found for the given selector. The selector must specify at least
+     * a project.
+     *
+     * @param selector the item selector that must specify at least a project
+     * @return the list of artifact versions
+     * @throws ContentAccessException if the access to the underlying storage failed
+     * @throws IllegalArgumentException if the selector does not have project information
+     */
+    List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException;
 
     /**
      * Return all the artifacts of a given content item (namespace, project, version)
@@ -288,14 +242,6 @@ public interface ManagedRepositoryContent extends RepositoryContent
     Stream<? extends Artifact> newArtifactStream( ContentItem item ) throws ContentAccessException;
 
 
-    /**
-     * Returns true, if the selector coordinates point to a existing item in the repository.
-     *
-     * @param selector the item selector
-     * @return <code>true</code>, if there exists such a item, otherwise <code>false</code>
-     */
-    boolean hasContent( ItemSelector selector );
-
     /**
      * Copies the artifact to the given destination coordinates
      *
@@ -306,26 +252,6 @@ public interface ManagedRepositoryContent extends RepositoryContent
     void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException, ContentAccessException;
 
 
-    /**
-     * Returns the item that matches the given path. The item at the path must not exist.
-     *
-     * @param path the path string that points to the item
-     * @return the content item if the path is a valid item path
-     * @throws LayoutException if the path is not valid for the repository layout
-     */
-    ContentItem toItem(String path) throws LayoutException;
-
-
-    /**
-     * Returns the item that matches the given asset path. The asset must not exist.
-     *
-     * @param assetPath the path to the artifact or directory
-     * @return the item, if it is a valid path for the repository layout
-     * @throws LayoutException if the path is not valid for the repository
-     */
-    ContentItem toItem(StorageAsset assetPath) throws LayoutException;
-
-
     /// *****************   End of new generation interface **********************
 
 
index 8efdd84618143cd6cb7db433682e40c1b21518d7..68aca2d8b885be4c0983efd894dd007cacd41ba0 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.archiva.repository.content;
  * under the License.
  */
 
+import org.apache.archiva.repository.ItemConversionException;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.UnsupportedConversionException;
 import org.apache.archiva.repository.storage.StorageAsset;
@@ -34,7 +35,7 @@ public interface ContentItem
      * @param <T>   the class or interface
      * @return the specific project implementation
      */
-    <T extends Project> T adapt( Class<T> clazz ) throws UnsupportedConversionException;
+    <T extends ContentItem> T adapt( Class<T> clazz ) throws ItemConversionException;
 
     /**
      * Returns <code>true</code>, if this project supports the given adaptor class.
@@ -43,7 +44,9 @@ public interface ContentItem
      * @param <T>   the type
      * @return <code>true/code>, if the implementation is supported, otherwise false
      */
-    <T extends Project> boolean supports( Class<T> clazz );
+    <T extends ContentItem> boolean hasCharacteristic( Class<T> clazz );
+
+    <T extends ContentItem> void setCharacteristic( Class<T> clazz, T item );
 
     /**
      * Additional attributes
index 32dc00ee5c89d92afbb69d4fec910dd4780a2fa3..5d315b54aff522dbf3f44ac7fa404f2778999a40 100644 (file)
@@ -28,12 +28,6 @@ package org.apache.archiva.repository.content;
 public interface DataItem extends ContentItem
 {
 
-    /**
-     * Returns the parent of the data item
-     * @return the parent item, which is either a Version, Project or Namespace
-     */
-    ContentItem getParent( );
-
     /**
      * Returns the identifier of the data item.
      * @return the identifier string
index 26b0449baf6e2b840e4da668d06c07c651777ffa..b65fa7400860d7ff513aaf2d1c296eca5a55d19b 100644 (file)
@@ -22,8 +22,6 @@ package org.apache.archiva.repository.content.base;
 import org.apache.archiva.repository.content.Artifact;
 import org.apache.archiva.repository.content.ArtifactType;
 import org.apache.archiva.repository.content.BaseArtifactTypes;
-import org.apache.archiva.repository.content.ContentItem;
-import org.apache.archiva.repository.content.DataItem;
 import org.apache.archiva.repository.content.Version;
 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
 import org.apache.archiva.repository.content.base.builder.ArtifactVersionBuilder;
@@ -52,7 +50,7 @@ import org.apache.commons.lang3.StringUtils;
  *
  * @author Martin Stockhammer <martin_s@apache.org>
  */
-public class ArchivaArtifact extends ArchivaContentItem implements Artifact
+public class ArchivaArtifact extends BaseContentItem implements Artifact
 {
     private String id;
     private String artifactVersion;
@@ -135,8 +133,6 @@ public class ArchivaArtifact extends ArchivaContentItem implements Artifact
     {
         if ( this == o ) return true;
         if ( o == null || getClass( ) != o.getClass( ) ) return false;
-        if ( !super.equals( o ) ) return false;
-
         ArchivaArtifact that = (ArchivaArtifact) o;
 
         if ( !id.equals( that.id ) ) return false;
index 7e15d61be6a21680cb20742b396e2a70635f5191..acba93b4fc86ddb71bce3912e286f07b3d83edbc 100644 (file)
@@ -21,105 +21,39 @@ package org.apache.archiva.repository.content.base;
 
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.content.ContentItem;
+import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
-import org.apache.archiva.repository.content.base.builder.OptBuilder;
+import org.apache.archiva.repository.content.Version;
+import org.apache.archiva.repository.content.base.builder.ArchivaContentItemOptBuilder;
 import org.apache.archiva.repository.content.base.builder.WithAssetBuilder;
-import org.apache.archiva.repository.content.base.builder.WithRepositoryBuilder;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Abstract implementation of ContentItem interface.
  * <p>
  * The attribute map is created, when the first values are put to the map.
  */
-public abstract class ArchivaContentItem implements ContentItem
+public class ArchivaContentItem extends BaseContentItem implements ContentItem
 {
 
-    private Map<String, String> attributes;
-    private ManagedRepositoryContent repository;
-    private StorageAsset asset;
-
-    @Override
-    public <T extends Project> T adapt( Class<T> clazz )
-    {
-        return (T) this;
-    }
-
-    @Override
-    public <T extends Project> boolean supports( Class<T> clazz )
-    {
-        return clazz != null && clazz.isAssignableFrom( this.getClass( ) );
-    }
-
-
-    /**
-     * Does lazy initialization of the attributes map.
-     * Returns a unmodifiable map.
-     *
-     * @return unmodifiable map of attributes
-     */
-    @Override
-    public Map<String, String> getAttributes( )
-    {
-        if ( this.attributes == null )
-        {
-            return Collections.emptyMap( );
-        }
-        else
-        {
-            return Collections.unmodifiableMap( this.attributes );
-        }
-    }
 
     /**
-     * Adds a attribute value. The key must not be <code>null</code>.
+     * Creates the builder for creating new archiva project instances.
+     * You have to set all required attributes before you can call the build() method.
      *
-     * @param key   the attribute key
-     * @param value the attribute value
-     * @throws IllegalArgumentException if the key is <code>null</code> or empty
+     * @param storageAsset the asset
+     * @return a builder instance
      */
-    public void putAttribute( String key, String value ) throws IllegalArgumentException
-    {
-        if ( this.attributes == null )
-        {
-            this.attributes = new HashMap<>( );
-        }
-        if ( StringUtils.isEmpty( key ) )
-        {
-            throw new IllegalArgumentException( "Key value must not be empty or null" );
-        }
-        this.attributes.put( key, value );
-    }
-
-    @Override
-    public String getAttribute( String key )
+    public static ArchivaContentItemOptBuilder withAsset( StorageAsset storageAsset )
     {
-        if ( this.attributes == null )
-        {
-            return null;
-        }
-        else
-        {
-            return this.attributes.get( key );
-        }
+        return new ArchivaContentItemBuilder().withAsset( storageAsset );
     }
 
-    @Override
-    public ManagedRepositoryContent getRepository( )
+    public static WithAssetBuilder<ArchivaContentItemOptBuilder> withRepository( ManagedRepositoryContent repository )
     {
-        return repository;
+        return new ArchivaContentItemBuilder().withRepository( repository );
     }
 
-    @Override
-    public StorageAsset getAsset( )
-    {
-        return asset;
-    }
 
     @Override
     public boolean equals( Object o )
@@ -150,86 +84,56 @@ public abstract class ArchivaContentItem implements ContentItem
      */
 
 
-    /**
-     * Builder for content item. Must be extended by subclasses.
-     * The builder uses chained interfaces for building the required attributes. That means you have to set
-     * some certain attributes, before you can build the content item instance via the {@link #build()} method.
-     * <p>
-     * Subclasses should extend from this class and provide the interface/class for the destination item,
-     * a interface for the optional attributes and a interface that is returned after the last required attribute is
-     * set.
-     * <p>
-     * The interface for optional attributes should inherit from {@link OptBuilder}
-     *
-     * @param <I> the item class that should be built
-     * @param <O> the class/interface for the optional attributes
-     * @param <N> the class/interface for the next (required) attribute after the base attributes are set
-     */
-    protected abstract static class ContentItemBuilder<I extends ArchivaContentItem, O extends OptBuilder<I, O>, N>
-        implements WithRepositoryBuilder, WithAssetBuilder<N>,
-        OptBuilder<I, O>
+    public static final class ArchivaContentItemBuilder extends ContentItemBuilder<ArchivaContentItem, ArchivaContentItemOptBuilder, ArchivaContentItemOptBuilder>
+        implements ArchivaContentItemOptBuilder
     {
 
-        protected I item;
-
-        protected ContentItemBuilder( I item )
+        private ArchivaContentItemBuilder(  )
         {
-            this.item = item;
+            super( new ArchivaContentItem() );
         }
 
-        protected abstract O getOptBuilder( );
-
-        protected abstract N getNextBuilder( );
+        @Override
+        public ArchivaContentItemOptBuilder getOptBuilder( )
+        {
+            return this;
+        }
 
         @Override
-        public WithAssetBuilder<N> withRepository( ManagedRepositoryContent repository )
+        public ArchivaContentItemOptBuilder getNextBuilder( )
         {
-            if ( repository == null )
-            {
-                throw new IllegalArgumentException( "Repository may not be null" );
-            }
-            ( (ArchivaContentItem) item ).repository = repository;
             return this;
         }
 
+
         @Override
-        public N withAsset( StorageAsset asset )
+        public ArchivaContentItemOptBuilder withNamespace( Namespace namespace )
         {
-            if ( asset == null )
-            {
-                throw new IllegalArgumentException( "Asset may not be null" );
-            }
-            ( (ArchivaContentItem) item ).asset = asset;
-            return getNextBuilder( );
+            item.setCharacteristic( Namespace.class, namespace );
+            return this;
         }
 
         @Override
-        public O withAttribute( String key, String value )
+        public ArchivaContentItemOptBuilder withProject( Project project )
         {
-            if ( StringUtils.isEmpty( key ) )
-            {
-                throw new IllegalArgumentException( "Attribute key may not be null" );
-            }
-            item.putAttribute( key, value );
-            return getOptBuilder( );
+            item.setCharacteristic( Project.class, project );
+            return this;
         }
 
-        protected void setRepository( ManagedRepositoryContent repository )
+        @Override
+        public ArchivaContentItemOptBuilder withVersion( Version version )
         {
-            ( (ArchivaContentItem) item ).repository = repository;
+            item.setCharacteristic( Version.class, version );
+            return this;
         }
 
         @Override
-        public I build( )
+        public ArchivaContentItem build( )
         {
+            super.build( );
             return item;
         }
-
     }
 
-    @Override
-    public boolean exists( )
-    {
-        return asset.exists( );
-    }
+
 }
index cb13e183266a0bf8c20b3e891fd61bc2311bd906..c825bb31688e999303c2dc6f411dd3176a332403 100644 (file)
@@ -19,9 +19,7 @@ package org.apache.archiva.repository.content.base;
  * under the License.
  */
 
-import org.apache.archiva.repository.content.BaseArtifactTypes;
 import org.apache.archiva.repository.content.BaseDataItemTypes;
-import org.apache.archiva.repository.content.ContentItem;
 import org.apache.archiva.repository.content.DataItem;
 import org.apache.archiva.repository.content.DataItemType;
 import org.apache.archiva.repository.content.base.builder.DataItemOptBuilder;
@@ -49,10 +47,9 @@ import org.apache.commons.lang3.StringUtils;
  *
  * @author Martin Stockhammer <martin_s@apache.org>
  */
-public class ArchivaDataItem extends ArchivaContentItem implements DataItem
+public class ArchivaDataItem extends BaseContentItem implements DataItem
 {
     private String id;
-    private ContentItem parent;
     private String contentType;
     private DataItemType dataItemType;
 
@@ -67,12 +64,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem
         return id;
     }
 
-    @Override
-    public ContentItem getParent( )
-    {
-        return parent;
-    }
-
     @Override
     public String getContentType( )
     {
@@ -109,7 +100,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem
         ArchivaDataItem that = (ArchivaDataItem) o;
 
         if ( !id.equals( that.id ) ) return false;
-        if ( !parent.equals( that.parent ) ) return false;
         return dataItemType.equals(that.dataItemType );
     }
 
@@ -118,7 +108,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem
     {
         int result = super.hashCode( );
         result = 31 * result + id.hashCode( );
-        result = 31 * result + parent.hashCode( );
         result = 31 * result + dataItemType.hashCode( );
         return result;
     }
@@ -126,9 +115,8 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem
     @Override
     public String toString( )
     {
-        final StringBuilder sb = new StringBuilder( "ArchivaArtifact{" );
+        final StringBuilder sb = new StringBuilder( "ArchivaDataItem{" );
         sb.append( "id='" ).append( id ).append( '\'' );
-        sb.append( ", parent=" ).append( parent );
         sb.append( ", contentType='" ).append( contentType ).append( '\'' );
         sb.append( ", artifactType=" ).append( dataItemType );
         sb.append( '}' );
@@ -167,18 +155,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem
             return this;
         }
 
-        @Override
-        public DataItemOptBuilder withParent( ContentItem parent )
-        {
-            if ( parent == null )
-            {
-                throw new IllegalArgumentException( "version may not be null" );
-            }
-            item.parent = parent;
-            super.setRepository( parent.getRepository( ) );
-            return this;
-        }
-
         @Override
         public DataItemOptBuilder withId( String id )
         {
index b85f25a60940317fcac2699e39fbd2274e1edfcc..9982ea0de55f3f43f33ec631413c2acedefb9f11 100644 (file)
@@ -38,7 +38,7 @@ import java.util.regex.PatternSyntaxException;
  * @author Martin Stockhammer <martin_s@apache.org>
  * @since 3.0
  */
-public class ArchivaNamespace extends ArchivaContentItem implements Namespace
+public class ArchivaNamespace extends BaseContentItem implements Namespace
 {
     private String namespace;
     private List<String> namespacePath;
@@ -72,10 +72,9 @@ public class ArchivaNamespace extends ArchivaContentItem implements Namespace
     {
         if ( this == o ) return true;
         if ( o == null || getClass( ) != o.getClass( ) ) return false;
-        if ( !super.equals( o ) ) return false;
-
         ArchivaNamespace that = (ArchivaNamespace) o;
 
+        if (!repository.equals( that.repository )) return false;
         return namespace.equals( that.namespace );
     }
 
index ac72f6217f28c51eda76bb1bd22fe717c5c3cda1..9fb9a0cf6b8bbb3e25187ea7502a9881a853e416 100644 (file)
@@ -19,11 +19,15 @@ package org.apache.archiva.repository.content.base;
  * under the License.
  */
 
+import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.base.builder.ProjectOptBuilder;
 import org.apache.archiva.repository.content.base.builder.ProjectWithIdBuilder;
+import org.apache.archiva.repository.content.base.builder.WithAssetBuilder;
+import org.apache.archiva.repository.content.base.builder.WithNamespaceBuilder;
 import org.apache.archiva.repository.content.base.builder.WithNamespaceObjectBuilder;
+import org.apache.archiva.repository.content.base.builder.WithProjectBuilder;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.lang3.StringUtils;
 
@@ -38,7 +42,7 @@ import org.apache.commons.lang3.StringUtils;
  * @author Martin Stockhammer <martin_s@apache.org>
  * @since 3.0
  */
-public class ArchivaProject extends ArchivaContentItem implements Project
+public class ArchivaProject extends BaseContentItem implements Project
 {
     private Namespace namespace;
     private String id;
@@ -62,6 +66,11 @@ public class ArchivaProject extends ArchivaContentItem implements Project
         return new Builder( ).withAsset( storageAsset );
     }
 
+    public static WithAssetBuilder<WithNamespaceObjectBuilder> withRepository( ManagedRepositoryContent repository )
+    {
+        return new ArchivaProject.Builder( ).withRepository( repository );
+    }
+
     @Override
     public Namespace getNamespace( )
     {
@@ -80,10 +89,10 @@ public class ArchivaProject extends ArchivaContentItem implements Project
     {
         if ( this == o ) return true;
         if ( o == null || getClass( ) != o.getClass( ) ) return false;
-        if ( !super.equals( o ) ) return false;
 
         ArchivaProject that = (ArchivaProject) o;
 
+        if (!repository.equals( that.repository )) return false;
         if ( !namespace.equals( that.namespace ) ) return false;
         return id.equals( that.id );
     }
@@ -100,7 +109,7 @@ public class ArchivaProject extends ArchivaContentItem implements Project
     @Override
     public String toString( )
     {
-        return id + ", namespace="+namespace.toString();
+        return "ArchivaProject{ "+id + ", namespace="+namespace.toString()+"}";
     }
 
     /*
index e10d3b1c52fefedc76928340af356a9ee855d0f3..c553c3bfc60944317209c2d2d862bfdba87301c0 100644 (file)
@@ -19,9 +19,12 @@ package org.apache.archiva.repository.content.base;
  * under the License.
  */
 
+import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.Version;
 import org.apache.archiva.repository.content.base.builder.VersionOptBuilder;
+import org.apache.archiva.repository.content.base.builder.WithAssetBuilder;
+import org.apache.archiva.repository.content.base.builder.WithNamespaceBuilder;
 import org.apache.archiva.repository.content.base.builder.WithProjectBuilder;
 import org.apache.archiva.repository.content.base.builder.WithVersionBuilder;
 import org.apache.archiva.repository.storage.StorageAsset;
@@ -41,7 +44,7 @@ import java.util.regex.PatternSyntaxException;
  * <p>
  * Two instances are equal, if the project and the version match in addition to the base attributes repository and asset.
  */
-public class ArchivaVersion extends ArchivaContentItem implements Version
+public class ArchivaVersion extends BaseContentItem implements Version
 {
 
     private String version;
@@ -66,6 +69,11 @@ public class ArchivaVersion extends ArchivaContentItem implements Version
         return new Builder( ).withAsset( storageAsset );
     }
 
+    public static WithAssetBuilder<WithProjectBuilder> withRepository( ManagedRepositoryContent repository )
+    {
+        return new ArchivaVersion.Builder( ).withRepository( repository );
+    }
+
     @Override
     public List<String> getVersionSegments( )
     {
@@ -90,10 +98,9 @@ public class ArchivaVersion extends ArchivaContentItem implements Version
     {
         if ( this == o ) return true;
         if ( o == null || getClass( ) != o.getClass( ) ) return false;
-        if ( !super.equals( o ) ) return false;
 
         ArchivaVersion that = (ArchivaVersion) o;
-
+        if (!repository.equals( that.repository )) return false;
         if ( !version.equals( that.version ) ) return false;
         return project.equals( that.project );
     }
@@ -110,7 +117,7 @@ public class ArchivaVersion extends ArchivaContentItem implements Version
     @Override
     public String toString( )
     {
-        return version+", project="+project.toString();
+        return "ArchivaVersion{ "+version+", project="+project.toString()+"}";
     }
 
     private static final class Builder extends ContentItemBuilder<ArchivaVersion, VersionOptBuilder, WithProjectBuilder>
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/BaseContentItem.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/BaseContentItem.java
new file mode 100644 (file)
index 0000000..3866131
--- /dev/null
@@ -0,0 +1,151 @@
+package org.apache.archiva.repository.content.base;
+
+/*
+ * 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.repository.ItemConversionException;
+import org.apache.archiva.repository.LayoutException;
+import org.apache.archiva.repository.ManagedRepositoryContent;
+import org.apache.archiva.repository.content.ContentItem;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public abstract class BaseContentItem implements ContentItem
+{
+    protected ManagedRepositoryContent repository;
+    protected StorageAsset asset;
+    private Map<String, String> attributes;
+    private Map<Class<?>, ContentItem> characteristics  = new HashMap<>(  );
+    private Set<Class<?>> failedConversions = new HashSet<>(  );
+
+    @Override
+    public <T extends ContentItem> T adapt( Class<T> clazz ) throws ItemConversionException
+    {
+        if (characteristics.containsKey( clazz )) {
+            return (T) characteristics.get( clazz );
+        } else {
+            for ( Map.Entry<Class<?>, ? extends ContentItem> cEntry : characteristics.entrySet()) {
+                if (clazz.isAssignableFrom( cEntry.getKey() )) {
+                    return (T) cEntry.getValue( );
+                }
+            }
+            try  {
+                return repository.applyCharacteristic( clazz, this );
+            } catch ( LayoutException e ) {
+                throw new ItemConversionException( "Could not convert item to " + clazz, e );
+            }
+        }
+    }
+
+    @Override
+    public <T extends ContentItem> boolean hasCharacteristic( Class<T> clazz )
+    {
+        if (clazz == null) {
+            return false;
+        }
+        if ( characteristics.containsKey( clazz )
+                || characteristics.keySet( ).stream( ).anyMatch( cClass -> clazz.isAssignableFrom( cClass ) ) ) {
+            return true;
+        };
+        return false;
+    }
+
+    /**
+     * Does lazy initialization of the attributes map.
+     * Returns a unmodifiable map.
+     *
+     * @return unmodifiable map of attributes
+     */
+    @Override
+    public Map<String, String> getAttributes( )
+    {
+        if ( this.attributes == null )
+        {
+            return Collections.emptyMap( );
+        }
+        else
+        {
+            return Collections.unmodifiableMap( this.attributes );
+        }
+    }
+
+    /**
+     * Adds a attribute value. The key must not be <code>null</code>.
+     *
+     * @param key   the attribute key
+     * @param value the attribute value
+     * @throws IllegalArgumentException if the key is <code>null</code> or empty
+     */
+    public void putAttribute( String key, String value ) throws IllegalArgumentException
+    {
+        if ( this.attributes == null )
+        {
+            this.attributes = new HashMap<>( );
+        }
+        if ( StringUtils.isEmpty( key ) )
+        {
+            throw new IllegalArgumentException( "Key value must not be empty or null" );
+        }
+        this.attributes.put( key, value );
+    }
+
+    @Override
+    public String getAttribute( String key )
+    {
+        if ( this.attributes == null )
+        {
+            return null;
+        }
+        else
+        {
+            return this.attributes.get( key );
+        }
+    }
+
+    @Override
+    public ManagedRepositoryContent getRepository( )
+    {
+        return repository;
+    }
+
+    @Override
+    public StorageAsset getAsset( )
+    {
+        return asset;
+    }
+
+    @Override
+    public boolean exists( )
+    {
+        return asset.exists( );
+    }
+
+    public <T extends ContentItem> void setCharacteristic( Class<T> clazz, T item )
+    {
+        this.characteristics.put( clazz, item );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ContentItemBuilder.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ContentItemBuilder.java
new file mode 100644 (file)
index 0000000..dd3a53d
--- /dev/null
@@ -0,0 +1,103 @@
+package org.apache.archiva.repository.content.base;
+
+/*
+ * 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.repository.ManagedRepositoryContent;
+import org.apache.archiva.repository.content.base.builder.OptBuilder;
+import org.apache.archiva.repository.content.base.builder.WithAssetBuilder;
+import org.apache.archiva.repository.content.base.builder.WithRepositoryBuilder;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Builder for content item. Must be extended by subclasses.
+ * The builder uses chained interfaces for building the required attributes. That means you have to set
+ * some certain attributes, before you can build the content item instance via the {@link #build()} method.
+ * <p>
+ * Subclasses should extend from this class and provide the interface/class for the destination item,
+ * a interface for the optional attributes and a interface that is returned after the last required attribute is
+ * set.
+ * <p>
+ * The interface for optional attributes should inherit from {@link OptBuilder}
+ *
+ * @param <I> the item class that should be built
+ * @param <O> the class/interface for the optional attributes
+ * @param <N> the class/interface for the next (required) attribute after the base attributes are set
+ */
+abstract class ContentItemBuilder<I extends BaseContentItem, O extends OptBuilder<I, O>, N>
+    implements WithRepositoryBuilder, WithAssetBuilder<N>,
+    OptBuilder<I, O>
+{
+
+    protected I item;
+
+    protected ContentItemBuilder( I item )
+    {
+        this.item = item;
+    }
+
+    protected abstract O getOptBuilder( );
+
+    protected abstract N getNextBuilder( );
+
+    @Override
+    public WithAssetBuilder<N> withRepository( ManagedRepositoryContent repository )
+    {
+        if ( repository == null )
+        {
+            throw new IllegalArgumentException( "Repository may not be null" );
+        }
+        ( (BaseContentItem) item ).repository = repository;
+        return this;
+    }
+
+    @Override
+    public N withAsset( StorageAsset asset )
+    {
+        if ( asset == null )
+        {
+            throw new IllegalArgumentException( "Asset may not be null" );
+        }
+        ( (BaseContentItem) item ).asset = asset;
+        return getNextBuilder( );
+    }
+
+    @Override
+    public O withAttribute( String key, String value )
+    {
+        if ( StringUtils.isEmpty( key ) )
+        {
+            throw new IllegalArgumentException( "Attribute key may not be null" );
+        }
+        item.putAttribute( key, value );
+        return getOptBuilder( );
+    }
+
+    protected void setRepository( ManagedRepositoryContent repository )
+    {
+        item.repository = repository;
+    }
+
+    @Override
+    public I build( )
+    {
+        return item;
+    }
+
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ArchivaContentItemOptBuilder.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ArchivaContentItemOptBuilder.java
new file mode 100644 (file)
index 0000000..36ca843
--- /dev/null
@@ -0,0 +1,38 @@
+package org.apache.archiva.repository.content.base.builder;
+
+/*
+ * 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.repository.content.Namespace;
+import org.apache.archiva.repository.content.Project;
+import org.apache.archiva.repository.content.Version;
+import org.apache.archiva.repository.content.base.ArchivaContentItem;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public interface ArchivaContentItemOptBuilder extends OptBuilder<ArchivaContentItem, ArchivaContentItemOptBuilder>
+{
+    ArchivaContentItemOptBuilder withNamespace( Namespace namespace );
+
+    ArchivaContentItemOptBuilder withProject( Project project );
+
+    ArchivaContentItemOptBuilder withVersion( Version version );
+
+    ArchivaContentItem build();
+}
index 04da17d133df729bc159434c1a6ea42e929c3a00..445946ec26e607c40a5df7079dda56b7b8dc1a93 100644 (file)
@@ -32,8 +32,6 @@ public interface DataItemOptBuilder
     extends OptBuilder<ArchivaDataItem, DataItemOptBuilder>
 {
 
-    DataItemOptBuilder withParent( ContentItem parent );
-
     DataItemOptBuilder withContentType( String contentType );
 
     DataItemOptBuilder withDataType( DataItemType type );
index dfd770380e1c6a2e3433ccf1f1dd99c07b12c75b..2e7871d00170a9faf6ae3f256184378773b972b9 100644 (file)
@@ -163,6 +163,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return null;
     }
 
+    @Override
+    public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
+    {
+        return null;
+    }
+
     @Override
     public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
@@ -181,6 +187,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return false;
     }
 
+    @Override
+    public ContentItem getParent( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public List<? extends ContentItem> getChildren( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public <T extends ContentItem> T applyCharacteristic( Class<T> clazz, ContentItem item ) throws LayoutException
+    {
+        return null;
+    }
+
     @Override
     public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException
     {
index 7bbf51736f1258df9eb2780cae34573859efaf1d..8a9442a365bcdcea2115a322b96b2f3ede0d618f 100644 (file)
@@ -163,6 +163,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return null;
     }
 
+    @Override
+    public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
+    {
+        return null;
+    }
+
     @Override
     public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
@@ -181,6 +187,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return false;
     }
 
+    @Override
+    public ContentItem getParent( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public List<? extends ContentItem> getChildren( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public <T extends ContentItem> T applyCharacteristic( Class<T> clazz, ContentItem item ) throws LayoutException
+    {
+        return null;
+    }
+
     @Override
     public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException
     {
index 6d3d60a0fba8bd5cfe4408de3c5b4d96b7bc2223..fea65d10fe624e7d7ef72a23f8d660ccf890f027 100644 (file)
@@ -47,6 +47,7 @@ import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
@@ -82,6 +83,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return null;
     }
 
+    @Override
+    public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException
+    {
+
+    }
+
     @Override
     public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
     {
@@ -130,6 +137,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return null;
     }
 
+    @Override
+    public Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException
+    {
+        return null;
+    }
+
     @Override
     public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
     {
@@ -154,6 +167,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return null;
     }
 
+    @Override
+    public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
+    {
+        return null;
+    }
+
     @Override
     public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
@@ -172,6 +191,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
         return false;
     }
 
+    @Override
+    public ContentItem getParent( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public List<? extends ContentItem> getChildren( ContentItem item )
+    {
+        return null;
+    }
+
+    @Override
+    public <T extends ContentItem> T applyCharacteristic( Class<T> clazz, ContentItem item ) throws LayoutException
+    {
+        return null;
+    }
+
     @Override
     public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException
     {
index af42632d64089a91e1a7313dcd29e5dc19672fdd..51e3018e33dc5724291ae86718250c93e4fc44b2 100644 (file)
@@ -24,24 +24,30 @@ import org.apache.archiva.configuration.FileTypes;
 import org.apache.archiva.metadata.maven.MavenMetadataReader;
 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
 import org.apache.archiva.model.ArchivaArtifact;
+import org.apache.archiva.model.ArchivaRepositoryMetadata;
 import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.model.ProjectReference;
 import org.apache.archiva.model.VersionedReference;
 import org.apache.archiva.repository.ContentAccessException;
 import org.apache.archiva.repository.ContentNotFoundException;
 import org.apache.archiva.repository.EditableManagedRepository;
+import org.apache.archiva.repository.ItemDeleteStatus;
 import org.apache.archiva.repository.LayoutException;
 import org.apache.archiva.repository.ManagedRepository;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.content.Artifact;
 import org.apache.archiva.repository.content.ArtifactType;
+import org.apache.archiva.repository.content.BaseArtifactTypes;
+import org.apache.archiva.repository.content.BaseDataItemTypes;
 import org.apache.archiva.repository.content.ContentItem;
+import org.apache.archiva.repository.content.DataItem;
+import org.apache.archiva.repository.content.DataItemType;
 import org.apache.archiva.repository.content.ItemNotFoundException;
 import org.apache.archiva.repository.content.ItemSelector;
-import org.apache.archiva.repository.content.BaseArtifactTypes;
 import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.Version;
+import org.apache.archiva.repository.content.base.ArchivaContentItem;
 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
 import org.apache.archiva.repository.content.base.ArchivaNamespace;
 import org.apache.archiva.repository.content.base.ArchivaProject;
@@ -49,6 +55,7 @@ import org.apache.archiva.repository.content.base.ArchivaVersion;
 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
 import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
+import org.apache.archiva.repository.metadata.RepositoryMetadataException;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.storage.util.StorageUtil;
@@ -57,6 +64,7 @@ import org.apache.commons.lang3.StringUtils;
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.naming.Name;
 import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
@@ -67,6 +75,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -86,7 +95,8 @@ public class ManagedDefaultRepositoryContent
 
     private FileTypes filetypes;
 
-    public void setFileTypes(FileTypes fileTypes) {
+    public void setFileTypes( FileTypes fileTypes )
+    {
         this.filetypes = fileTypes;
     }
 
@@ -95,7 +105,7 @@ public class ManagedDefaultRepositoryContent
     private FileLockManager lockManager;
 
     @Inject
-    @Named("repositoryPathTranslator#maven2")
+    @Named( "repositoryPathTranslator#maven2" )
     private RepositoryPathTranslator pathTranslator;
 
     @Inject
@@ -121,17 +131,17 @@ public class ManagedDefaultRepositoryContent
      * the hierarchical structure.
      * TODO: Better use a object cache? E.g. our spring cache implementation?
      */
-    private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
-    private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
-    private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
-    private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
+    private ReferenceMap<StorageAsset, ContentItem> itemMap = new ReferenceMap<>( );
+    private ReferenceMap<StorageAsset, DataItem> dataItemMap = new ReferenceMap<>( );
 
-    public ManagedDefaultRepositoryContent() {
-        super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
+    public ManagedDefaultRepositoryContent( )
+    {
+        super( Collections.singletonList( new DefaultArtifactMappingProvider( ) ) );
     }
 
-    public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
-        super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
+    public ManagedDefaultRepositoryContent( ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager )
+    {
+        super( Collections.singletonList( new DefaultArtifactMappingProvider( ) ) );
         setFileTypes( fileTypes );
         this.lockManager = lockManager;
         setRepository( repository );
@@ -139,40 +149,74 @@ public class ManagedDefaultRepositoryContent
 
     public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
     {
-        super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
+        super( artifactMappingProviders == null ? Collections.singletonList( new DefaultArtifactMappingProvider( ) ) : artifactMappingProviders );
         setFileTypes( fileTypes );
         this.lockManager = lockManager;
         setRepository( repository );
 
     }
 
-    private StorageAsset getAssetByPath(String assetPath) {
+    private StorageAsset getAssetByPath( String assetPath )
+    {
         return getStorage( ).getAsset( assetPath );
     }
 
-    private StorageAsset getAsset(String namespace) {
-        String namespacePath = formatAsDirectory( namespace.trim() );
-        if (StringUtils.isEmpty( namespacePath )) {
+    private StorageAsset getAsset( String namespace )
+    {
+        String namespacePath = formatAsDirectory( namespace.trim( ) );
+        if ( StringUtils.isEmpty( namespacePath ) )
+        {
             namespacePath = "";
         }
-        return getAssetByPath(namespacePath);
+        return getAssetByPath( namespacePath );
     }
 
-    private StorageAsset getAsset(String namespace, String project) {
+    private StorageAsset getAsset( String namespace, String project )
+    {
         return getAsset( namespace ).resolve( project );
     }
 
-    private StorageAsset getAsset(String namespace, String project, String version) {
+    private StorageAsset getAsset( String namespace, String project, String version )
+    {
         return getAsset( namespace, project ).resolve( version );
     }
 
-    private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
+    private StorageAsset getAsset( String namespace, String project, String version, String fileName )
+    {
         return getAsset( namespace, project, version ).resolve( fileName );
     }
 
 
     /// ************* Start of new generation interface ******************
 
+
+    @Override
+    public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException
+    {
+        try ( Stream<? extends ContentItem> stream = newItemStream( selector, false ) )
+        {
+            stream.forEach( item -> {
+                try
+                {
+                    deleteItem( item );
+                    consumer.accept( new ItemDeleteStatus( item ) );
+                }
+                catch ( ItemNotFoundException e )
+                {
+                    consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.ITEM_NOT_FOUND, e ) );
+                }
+                catch ( Exception e )
+                {
+                    consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.DELETION_FAILED, e ) );
+                }
+                catch ( Throwable e )
+                {
+                    consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.UNKNOWN, e ) );
+                }
+            } );
+        }
+    }
+
     /**
      * Removes the item from the filesystem. For namespaces, projects and versions it deletes
      * recursively.
@@ -180,11 +224,11 @@ public class ManagedDefaultRepositoryContent
      * parallel to projects. Which means deleting a namespaces also deletes the sub namespaces and
      * not only the projects of the given namespace. Better run the delete for each project of
      * a namespace.
-     *
+     * <p>
      * Artifacts are deleted as provided. No related artifacts will be deleted.
      *
      * @param item the item that should be removed
-     * @throws ItemNotFoundException if the item does not exist
+     * @throws ItemNotFoundException  if the item does not exist
      * @throws ContentAccessException if some error occurred while accessing the filesystem
      */
     @Override
@@ -194,9 +238,9 @@ public class ManagedDefaultRepositoryContent
         final Path itemPath = item.getAsset( ).getFilePath( );
         if ( !Files.exists( itemPath ) )
         {
-            throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
+            throw new ItemNotFoundException( "The item " + item.toString( ) + "does not exist in the repository " + getId( ) );
         }
-        if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
+        if ( !itemPath.toAbsolutePath( ).startsWith( baseDirectory.toAbsolutePath( ) ) )
         {
             log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
             log.error( "Namespace directory: {}", itemPath );
@@ -205,10 +249,12 @@ public class ManagedDefaultRepositoryContent
         }
         try
         {
-            if (Files.isDirectory( itemPath ))
+            if ( Files.isDirectory( itemPath ) )
             {
                 FileUtils.deleteDirectory( itemPath );
-            } else {
+            }
+            else
+            {
                 Files.deleteIfExists( itemPath );
             }
         }
@@ -222,13 +268,20 @@ public class ManagedDefaultRepositoryContent
     @Override
     public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
     {
-        if (selector.hasVersion() && selector.hasArtifactId()) {
+        if ( selector.hasVersion( ) && selector.hasArtifactId( ) )
+        {
             return getArtifact( selector );
-        } else if (selector.hasProjectId() && selector.hasVersion()) {
+        }
+        else if ( selector.hasProjectId( ) && selector.hasVersion( ) )
+        {
             return getVersion( selector );
-        } else if (selector.hasProjectId()) {
+        }
+        else if ( selector.hasProjectId( ) )
+        {
             return getProject( selector );
-        } else {
+        }
+        else
+        {
             return getNamespace( selector );
         }
     }
@@ -236,191 +289,199 @@ public class ManagedDefaultRepositoryContent
     @Override
     public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
     {
-        return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
-            namespace -> {
-                StorageAsset nsPath = getAsset( namespace );
-                return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
-                    withNamespace( namespace ).build( );
-            });
+        StorageAsset nsPath = getAsset( namespaceSelector.getNamespace() );
+        return getNamespaceFromPath( nsPath );
     }
 
 
     @Override
     public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
     {
-        if (!selector.hasProjectId()) {
+        if ( !selector.hasProjectId( ) )
+        {
             throw new IllegalArgumentException( "Project id must be set" );
         }
         final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
-        return projectMap.computeIfAbsent( path, projectPath -> {
-            final Namespace ns = getNamespace( selector );
-            return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
-        }
-        );
+        return getProjectFromPath( path );
     }
 
 
     @Override
     public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
     {
-        if (!selector.hasProjectId()) {
+        if ( !selector.hasProjectId( ) )
+        {
             throw new IllegalArgumentException( "Project id must be set" );
         }
-        if (!selector.hasVersion() ) {
+        if ( !selector.hasVersion( ) )
+        {
             throw new IllegalArgumentException( "Version must be set" );
         }
-        final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
-        return versionMap.computeIfAbsent( path, versionPath -> {
-            final Project project = getProject( selector );
-            return ArchivaVersion.withAsset( path )
-                .withProject( project )
-                .withVersion( selector.getVersion( ) ).build();
-        } );
+        final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
+        return getVersionFromPath( path );
     }
 
 
-
-
-
-    public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
-        final String classifier, final String extension) {
-        Version version = getVersion(selector);
+    public Artifact createArtifact( final StorageAsset artifactPath, final ItemSelector selector,
+                                    final String classifier, final String extension )
+    {
+        Version version = getVersion( selector );
         ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
             .withVersion( version )
             .withId( selector.getArtifactId( ) )
             .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
             .withClassifier( classifier );
-        if (selector.hasType()) {
+        if ( selector.hasType( ) )
+        {
             builder.withType( selector.getType( ) );
         }
         return builder.build( );
     }
 
-    public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
+    public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath )
+    {
         final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
-        final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
-        return namespaceMap.computeIfAbsent( namespace,
-            myNamespace -> ArchivaNamespace.withRepository( this )
-                .withAsset( namespacePath )
-                .withNamespace( namespace )
-                .build( ) );
+        return getNamespaceFromPath( namespacePath );
     }
 
-    public Namespace getNamespaceFromPath( final StorageAsset namespacePath) {
+    public Namespace getNamespaceFromPath( final StorageAsset nsPath )
+    {
+        ContentItem item = itemMap.computeIfAbsent( nsPath,
+            path -> createNamespaceFromPath( nsPath ) );
+        if (!item.hasCharacteristic( Namespace.class )) {
+            item.setCharacteristic( Namespace.class, createNamespaceFromPath( nsPath ) );
+        }
+        return item.adapt( Namespace.class );
+    }
+
+    public Namespace createNamespaceFromPath( final StorageAsset namespacePath) {
         final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
-        return namespaceMap.computeIfAbsent( namespace,
-            myNamespace -> ArchivaNamespace.withRepository( this )
-                .withAsset( namespacePath )
-                .withNamespace( namespace )
-                .build( ) );
-    }
-
-    private Project getProjectFromPath( final StorageAsset projectPath) {
-        return projectMap.computeIfAbsent( projectPath,
-            myProjectPath -> ArchivaProject.withAsset( projectPath )
-                .withNamespace( getNamespaceFromPath( projectPath.getParent() ) )
-                .withId( projectPath.getName( ) ).build( )
+        return ArchivaNamespace.withRepository( this )
+            .withAsset( namespacePath )
+            .withNamespace( namespace )
+            .build( );
+    }
+
+    private Project getProjectFromPath( final StorageAsset path )
+    {
+        ContentItem item = itemMap.computeIfAbsent( path, projectPath ->
+                createProjectFromPath( projectPath )
         );
+        if (!item.hasCharacteristic( Project.class )) {
+            item.setCharacteristic( Project.class, createProjectFromPath( path ) );
+        }
+        return item.adapt( Project.class );
+    }
+
+    private Project createProjectFromPath( final StorageAsset projectPath ) {
+        Namespace namespace = getNamespaceFromPath( projectPath.getParent( ) );
+        return ArchivaProject.withRepository( this ).withAsset( projectPath )
+            .withNamespace( namespace )
+            .withId( projectPath.getName( ) ).build( );
     }
 
-    private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
+    private Project getProjectFromArtifactPath( final StorageAsset artifactPath )
+    {
         final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
-        return projectMap.computeIfAbsent( projectPath,
-            myProjectPath -> ArchivaProject.withAsset( projectPath )
-                .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
-                .withId( projectPath.getName( ) ).build( )
-        );
+        return getProjectFromPath( projectPath );
     }
 
-    private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
+    private Version getVersionFromArtifactPath( final StorageAsset artifactPath )
+    {
         final StorageAsset versionPath = artifactPath.getParent( );
-        return versionMap.computeIfAbsent( versionPath,
-            myVersionPath -> ArchivaVersion.withAsset( versionPath )
-                .withProject( getProjectFromArtifactPath( artifactPath ) )
-                .withVersion( versionPath.getName( ) ).build( ) );
+        return getVersionFromPath( versionPath );
     }
 
-    private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
+    private Version getVersionFromPath( StorageAsset path )
+    {
+        ContentItem item = itemMap.computeIfAbsent( path, versionPath ->
+            createVersionFromPath( versionPath )
+        );
+        if (!item.hasCharacteristic( Version.class )) {
+            item.setCharacteristic( Version.class, createVersionFromPath( path ) );
+        }
+        return item.adapt( Version.class );
+    }
+
+    private Version createVersionFromPath(StorageAsset path) {
+        Project proj = getProjectFromPath( path.getParent( ) );
+        return ArchivaVersion.withRepository( this ).withAsset( path )
+            .withProject( proj ).withVersion(path.getName()).build();
+    }
+
+    private Artifact getArtifactFromPath( final StorageAsset artifactPath )
+    {
+        DataItem item = dataItemMap.computeIfAbsent( artifactPath, myArtifactPath ->
+            createArtifactFromPath( myArtifactPath )
+        );
+        if (!item.hasCharacteristic( Artifact.class )) {
+            item.setCharacteristic( Artifact.class, createArtifactFromPath( artifactPath ) );
+        }
+        return item.adapt( Artifact.class );
+    }
+
+    private Artifact createArtifactFromPath( final StorageAsset artifactPath ) {
         final Version version = getVersionFromArtifactPath( artifactPath );
-        final ArtifactInfo info  = getArtifactInfoFromPath( version.getVersion(), artifactPath );
-        return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
-            org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
-                .withVersion( version )
-                .withId( info.id )
-                .withClassifier( info.classifier )
-                .withRemainder( info.remainder )
-                .withType( info.type )
-                .withArtifactVersion( info.version )
-                .withContentType( info.contentType )
-                .withArtifactType( info.artifactType )
+        final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion( ), artifactPath );
+        return org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
+            .withVersion( version )
+            .withId( info.id )
+            .withClassifier( info.classifier )
+            .withRemainder( info.remainder )
+            .withType( info.type )
+            .withArtifactVersion( info.version )
+            .withContentType( info.contentType )
+            .withArtifactType( info.artifactType )
+            .build( );
+    }
+
+    private String getContentType(StorageAsset artifactPath) {
+        try
+        {
+            return Files.probeContentType( artifactPath.getFilePath( ) );
+
+        }
+        catch ( IOException e )
+        {
+            return "";
+        }
+    }
+
+    private DataItem getDataItemFromPath( final StorageAsset artifactPath )
+    {
+        final String extension = StringUtils.substringAfterLast( artifactPath.getName( ), "." );
+        final String contentType = getContentType( artifactPath );
+        return dataItemMap.computeIfAbsent( artifactPath, myArtifactPath ->
+            org.apache.archiva.repository.content.base.ArchivaDataItem.withAsset( artifactPath )
+                .withId( artifactPath.getName( ) )
+                .withContentType( contentType )
                 .build( )
         );
+
     }
 
-    private ContentItem getItemFromPath(final StorageAsset itemPath) {
-        if (itemPath.isLeaf()) {
-            return getArtifactFromPath( itemPath );
-        } else {
-            if (versionMap.containsKey( itemPath )) {
-                return versionMap.get( itemPath );
-            }
-            if (projectMap.containsKey( itemPath )) {
-                return projectMap.get( itemPath );
-            }
-            String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
-            if (namespaceMap.containsKey( ns )) {
-                return namespaceMap.get( ns );
+    private ContentItem getItemFromPath( final StorageAsset itemPath )
+    {
+        if ( itemPath.isLeaf( ) )
+        {
+            if (dataItemMap.containsKey( itemPath )) {
+                return dataItemMap.get( itemPath );
             }
-            // No cached item, so we have to gather more information:
-            // Check for version directory (contains at least a pom or metadata file)
-            if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
-                n.endsWith( ".pom" )
-            )) {
-                return versionMap.computeIfAbsent( itemPath,
-                    myVersionPath -> ArchivaVersion.withAsset( itemPath )
-                        .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
-                        .withVersion( itemPath.getName() ).build());
+            return getDataItemFromPath( itemPath );
+        }
+        else
+        {
+            if (itemMap.containsKey( itemPath )) {
+                return itemMap.get( itemPath );
             } else {
-                // We have to dig further and find the next directory with a pom
-                Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
-                    .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
-                        || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
-                    .findFirst( );
-                if (foundFile.isPresent())
-                {
-                    int level = 0;
-                    StorageAsset current = foundFile.get( );
-                    while (current.hasParent() && !current.equals(itemPath)) {
-                        level++;
-                        current = current.getParent( );
-                    }
-                    // Project path if it is one level up from the found file
-                    if (level==2) {
-                        return projectMap.computeIfAbsent( itemPath,
-                            myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
-                    } else {
-                        // All other paths are treated as namespace
-                        return namespaceMap.computeIfAbsent( ns,
-                            myNamespace -> ArchivaNamespace.withRepository( this )
-                                .withAsset( itemPath )
-                                .withNamespace( ns )
-                                .build( ) );
-                    }
-                } else {
-                    // Don't know what to do with it, so we treat it as namespace path
-                    return namespaceMap.computeIfAbsent( ns,
-                        myNamespace -> ArchivaNamespace.withRepository( this )
-                            .withAsset( itemPath )
-                            .withNamespace( ns )
-                            .build( ) );
-                }
-
+                return ArchivaContentItem.withRepository( this ).withAsset( itemPath ).build();
             }
         }
     }
 
     // Simple object to hold artifact information
-    private class ArtifactInfo  {
+    private class ArtifactInfo
+    {
         private String id;
         private String version;
         private String extension;
@@ -432,7 +493,8 @@ public class ManagedDefaultRepositoryContent
         private ArtifactType artifactType = BaseArtifactTypes.MAIN;
     }
 
-    private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
+    private ArtifactInfo getArtifactInfoFromPath( String genericVersion, StorageAsset path )
+    {
         final ArtifactInfo info = new ArtifactInfo( );
         info.asset = path;
         info.id = path.getParent( ).getParent( ).getName( );
@@ -440,51 +502,64 @@ public class ManagedDefaultRepositoryContent
         if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
         {
             String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
-            String prefix = info.id+"-"+baseVersion+"-";
-            if (fileName.startsWith( prefix ))
+            String prefix = info.id + "-" + baseVersion + "-";
+            if ( fileName.startsWith( prefix ) )
             {
                 String versionPostfix = StringUtils.removeStart( fileName, prefix );
                 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
-                if (matcher.matches()) {
+                if ( matcher.matches( ) )
+                {
                     info.version = baseVersion + "-" + matcher.group( 1 );
                     String newPrefix = info.id + "-" + info.version;
-                    if (fileName.startsWith( newPrefix ))
+                    if ( fileName.startsWith( newPrefix ) )
                     {
                         String classPostfix = StringUtils.removeStart( fileName, newPrefix );
                         Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
-                        if (cMatch.matches()) {
+                        if ( cMatch.matches( ) )
+                        {
                             info.classifier = cMatch.group( 1 );
                             info.remainder = cMatch.group( 2 );
-                        } else {
+                        }
+                        else
+                        {
                             info.classifier = "";
                             info.remainder = classPostfix;
                         }
-                    } else {
+                    }
+                    else
+                    {
                         log.debug( "Artifact does not match the maven name pattern {}", path );
                         info.artifactType = BaseArtifactTypes.UNKNOWN;
                         info.classifier = "";
                         info.remainder = StringUtils.substringAfter( fileName, prefix );
                     }
-                } else {
+                }
+                else
+                {
                     log.debug( "Artifact does not match the snapshot version pattern {}", path );
 
                     info.artifactType = BaseArtifactTypes.UNKNOWN;
                     // This is just a guess. No guarantee to the get a usable version.
                     info.version = StringUtils.removeStart( fileName, info.id + '-' );
-                    String postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
-                    while (COMMON_EXTENSIONS.matcher(postfix).matches()) {
+                    String postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase( );
+                    while ( COMMON_EXTENSIONS.matcher( postfix ).matches( ) )
+                    {
                         info.version = StringUtils.substringBeforeLast( info.version, "." );
-                        postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
+                        postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase( );
                     }
                     info.classifier = "";
                     info.remainder = StringUtils.substringAfter( fileName, prefix );
                 }
-            } else {
+            }
+            else
+            {
                 log.debug( "Artifact does not match the maven name pattern: {}", path );
-                if ( fileName.contains( "-"+baseVersion ) )
+                if ( fileName.contains( "-" + baseVersion ) )
+                {
+                    info.id = StringUtils.substringBefore( fileName, "-" + baseVersion );
+                }
+                else
                 {
-                    info.id = StringUtils.substringBefore( fileName, "-"+baseVersion );
-                } else {
                     info.id = fileName;
                 }
                 info.artifactType = BaseArtifactTypes.UNKNOWN;
@@ -492,24 +567,34 @@ public class ManagedDefaultRepositoryContent
                 info.classifier = "";
                 info.remainder = StringUtils.substringAfterLast( fileName, "." );
             }
-        } else {
-            String prefix = info.id+"-"+genericVersion;
-            if (fileName.startsWith( prefix ))
+        }
+        else
+        {
+            String prefix = info.id + "-" + genericVersion;
+            if ( fileName.startsWith( prefix ) )
             {
-                info.version=genericVersion;
+                info.version = genericVersion;
                 String classPostfix = StringUtils.removeStart( fileName, prefix );
                 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
-                if (cMatch.matches()) {
+                if ( cMatch.matches( ) )
+                {
                     info.classifier = cMatch.group( 1 );
                     info.remainder = cMatch.group( 2 );
-                } else {
+                }
+                else
+                {
                     info.classifier = "";
                     info.remainder = classPostfix;
                 }
-            } else {
-                if (fileName.contains( "-"+genericVersion )) {
-                    info.id = StringUtils.substringBefore( fileName, "-"+genericVersion );
-                } else {
+            }
+            else
+            {
+                if ( fileName.contains( "-" + genericVersion ) )
+                {
+                    info.id = StringUtils.substringBefore( fileName, "-" + genericVersion );
+                }
+                else
+                {
                     info.id = fileName;
                 }
                 log.debug( "Artifact does not match the version pattern {}", path );
@@ -521,19 +606,27 @@ public class ManagedDefaultRepositoryContent
         }
         info.extension = StringUtils.substringAfterLast( fileName, "." );
         info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
-        try {
+        try
+        {
             info.contentType = Files.probeContentType( path.getFilePath( ) );
-        } catch (IOException e) {
+        }
+        catch ( IOException e )
+        {
             info.contentType = "";
             //
         }
-        if (MavenContentHelper.METADATA_FILENAME.equalsIgnoreCase( fileName )) {
+        if ( MavenContentHelper.METADATA_FILENAME.equalsIgnoreCase( fileName ) )
+        {
             info.artifactType = BaseArtifactTypes.METADATA;
-        } else if (MavenContentHelper.METADATA_REPOSITORY_FILENAME.equalsIgnoreCase( fileName )) {
+        }
+        else if ( MavenContentHelper.METADATA_REPOSITORY_FILENAME.equalsIgnoreCase( fileName ) )
+        {
             info.artifactType = MavenTypes.REPOSITORY_METADATA;
-        } else if (StringUtils.isNotEmpty( info.remainder ) && StringUtils.countMatches( info.remainder, "." )>=2) {
+        }
+        else if ( StringUtils.isNotEmpty( info.remainder ) && StringUtils.countMatches( info.remainder, "." ) >= 2 )
+        {
             String mainFile = StringUtils.substringBeforeLast( fileName, "." );
-            if (path.getParent().resolve( mainFile ).exists())
+            if ( path.getParent( ).resolve( mainFile ).exists( ) )
             {
                 info.artifactType = BaseArtifactTypes.RELATED;
             }
@@ -545,17 +638,20 @@ public class ManagedDefaultRepositoryContent
     @Override
     public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
     {
-        if (!selector.hasProjectId( )) {
+        if ( !selector.hasProjectId( ) )
+        {
             throw new IllegalArgumentException( "Project id must be set" );
         }
-        if (!selector.hasVersion( )) {
+        if ( !selector.hasVersion( ) )
+        {
             throw new IllegalArgumentException( "Version must be set" );
         }
-        if (!selector.hasArtifactId( )) {
+        if ( !selector.hasArtifactId( ) )
+        {
             throw new IllegalArgumentException( "Artifact id must be set" );
         }
-        final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
-            selector.getVersion());
+        final StorageAsset artifactDir = getAsset( selector.getNamespace( ), selector.getProjectId( ),
+            selector.getVersion( ) );
         final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
         final String classifier = MavenContentHelper.getClassifier( selector );
         final String extension = MavenContentHelper.getArtifactExtension( selector );
@@ -563,7 +659,7 @@ public class ManagedDefaultRepositoryContent
         final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
         final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
             selector.getVersion( ), fileName );
-        return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
+        return getArtifactFromPath( path );
     }
 
     /**
@@ -575,7 +671,7 @@ public class ManagedDefaultRepositoryContent
         return namespace.getAsset( ).list( ).stream( )
             .filter( a -> a.isContainer( ) )
             .map( a -> getProjectFromPath( a ) )
-            .collect( Collectors.toList());
+            .collect( Collectors.toList( ) );
     }
 
     @Override
@@ -586,6 +682,7 @@ public class ManagedDefaultRepositoryContent
 
     /**
      * Returns a version object for each directory that is a direct child of the project directory.
+     *
      * @param project the project for which the versions should be returned
      * @return the list of versions or a empty list, if not version was found
      */
@@ -596,41 +693,70 @@ public class ManagedDefaultRepositoryContent
         return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
             .map( a -> ArchivaVersion.withAsset( a )
                 .withProject( project )
-                .withVersion( a.getName() ).build() )
+                .withVersion( a.getName( ) ).build( ) )
             .collect( Collectors.toList( ) );
     }
 
     /**
-     * If the selector specifies a version, all artifact versions are returned, which means for snapshot
-     * versions the artifact versions are returned too.
+     * Returns the versions that can be found for the given selector.
      *
      * @param selector the item selector. At least namespace and projectId must be set.
      * @return the list of version objects or a empty list, if the selector does not match a version
-     * @throws ContentAccessException if the access to the underlying backend failed
+     * @throws ContentAccessException   if the access to the underlying backend failed
      * @throws IllegalArgumentException if the selector has no projectId specified
      */
     @Override
     public List<? extends Version> getVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
     {
-        if (!selector.hasProjectId()) {
+        if ( !selector.hasProjectId( ) )
+        {
             log.error( "Bad item selector for version list: {}", selector );
             throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
         }
         final Project project = getProject( selector );
-        if (selector.hasVersion()) {
+        if ( selector.hasVersion( ) )
+        {
             final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
             return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
                 .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
-                .map( v -> ArchivaVersion.withAsset( v.asset.getParent() )
-                    .withProject( project ).withVersion( v.version )
-                    .withAttribute(SNAPSHOT_ARTIFACT_VERSION,"true").build() )
-                .distinct()
+                .map( v -> getVersionFromArtifactPath( v.asset ) )
+                .distinct( )
                 .collect( Collectors.toList( ) );
-        } else {
+        }
+        else
+        {
             return getVersions( project );
         }
     }
 
+    public List<String> getArtifactVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
+    {
+        if ( !selector.hasProjectId( ) )
+        {
+            log.error( "Bad item selector for version list: {}", selector );
+            throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
+        }
+        final Project project = getProject( selector );
+        if ( selector.hasVersion( ) )
+        {
+            final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
+            return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
+                .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
+                .map( v -> v.version )
+                .distinct( )
+                .collect( Collectors.toList( ) );
+        }
+        else
+        {
+            return project.getAsset( ).list( ).stream( ).map( a -> getVersionFromPath( a ) )
+                .flatMap( v -> v.getAsset( ).list( ).stream( ).map( a -> getArtifactInfoFromPath( v.getVersion( ), a ) ) )
+                .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
+                .map( v -> v.version )
+                .distinct( )
+                .collect( Collectors.toList( ) );
+        }
+    }
+
 
     /**
      * See {@link #newArtifactStream(ItemSelector)}. This method collects the stream into a list.
@@ -642,8 +768,9 @@ public class ManagedDefaultRepositoryContent
     @Override
     public List<? extends Artifact> getArtifacts( ItemSelector selector ) throws ContentAccessException
     {
-        try(Stream<? extends Artifact> stream = newArtifactStream( selector )) {
-            return stream.collect( Collectors.toList());
+        try ( Stream<? extends Artifact> stream = newArtifactStream( selector ) )
+        {
+            return stream.collect( Collectors.toList( ) );
         }
     }
 
@@ -651,33 +778,44 @@ public class ManagedDefaultRepositoryContent
     /*
      * File filter to select certain artifacts using the selector data.
      */
-    private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
+    private Predicate<StorageAsset> getArtifactFileFilterFromSelector( final ItemSelector selector )
+    {
         Predicate<StorageAsset> p = a -> a.isLeaf( );
-        StringBuilder fileNamePattern = new StringBuilder("^" );
-        if (selector.hasArtifactId()) {
-            fileNamePattern.append( Pattern.quote(selector.getArtifactId( )) ).append("-");
-        } else {
-            fileNamePattern.append("[A-Za-z0-9_\\-.]+-");
+        StringBuilder fileNamePattern = new StringBuilder( "^" );
+        if ( selector.hasArtifactId( ) )
+        {
+            fileNamePattern.append( Pattern.quote( selector.getArtifactId( ) ) ).append( "-" );
+        }
+        else
+        {
+            fileNamePattern.append( "[A-Za-z0-9_\\-.]+-" );
         }
-        if (selector.hasArtifactVersion()) {
-            if ( selector.getArtifactVersion( ).contains("*")) {
+        if ( selector.hasArtifactVersion( ) )
+        {
+            if ( selector.getArtifactVersion( ).contains( "*" ) )
+            {
                 String[] tokens = StringUtils.splitByWholeSeparator( selector.getArtifactVersion( ), "*" );
-                for (String currentToken : tokens) {
-                    if (!currentToken.equals("")) {
+                for ( String currentToken : tokens )
+                {
+                    if ( !currentToken.equals( "" ) )
+                    {
                         fileNamePattern.append( Pattern.quote( currentToken ) );
                     }
                     fileNamePattern.append( "[A-Za-z0-9_\\-.]*" );
                 }
-            } else
+            }
+            else
             {
                 fileNamePattern.append( Pattern.quote( selector.getArtifactVersion( ) ) );
             }
-        } else  {
+        }
+        else
+        {
             fileNamePattern.append( "[A-Za-z0-9_\\-.]+" );
         }
         String classifier = selector.hasClassifier( ) ? selector.getClassifier( ) :
             ( selector.hasType( ) ? MavenContentHelper.getClassifierFromType( selector.getType( ) ) : null );
-        if (classifier != null)
+        if ( classifier != null )
         {
             if ( "*".equals( classifier ) )
             {
@@ -685,25 +823,32 @@ public class ManagedDefaultRepositoryContent
             }
             else
             {
-                fileNamePattern.append("-").append( Pattern.quote( classifier ) ).append( "\\." );
+                fileNamePattern.append( "-" ).append( Pattern.quote( classifier ) ).append( "\\." );
             }
-        } else {
+        }
+        else
+        {
             fileNamePattern.append( "\\." );
         }
         String extension = selector.hasExtension( ) ? selector.getExtension( ) :
             ( selector.hasType( ) ? MavenContentHelper.getArtifactExtension( selector ) : null );
-        if (extension != null) {
-            if (selector.includeRelatedArtifacts())
+        if ( extension != null )
+        {
+            if ( selector.includeRelatedArtifacts( ) )
+            {
+                fileNamePattern.append( Pattern.quote( extension ) ).append( "(\\.[A-Za-z0-9]+)?" );
+            }
+            else
             {
-                fileNamePattern.append( Pattern.quote( extension ) ).append("(\\.[A-Za-z0-9]+)?");
-            } else {
                 fileNamePattern.append( Pattern.quote( extension ) );
             }
-        } else {
+        }
+        else
+        {
             fileNamePattern.append( "[A-Za-z0-9.]+" );
         }
-        final Pattern pattern = Pattern.compile( fileNamePattern.toString() );
-        return p.and( a -> pattern.matcher( a.getName( ) ).matches());
+        final Pattern pattern = Pattern.compile( fileNamePattern.toString( ) );
+        return p.and( a -> pattern.matcher( a.getName( ) ).matches( ) );
     }
 
 
@@ -725,11 +870,11 @@ public class ManagedDefaultRepositoryContent
      *     To select only artifacts that match the layout you should add the artifact id and artifact version
      *     (can contain a '*' pattern).</li>
      * </ul>
-     *
+     * <p>
      * The '*' pattern can be used in classifiers and artifact versions and match zero or more characters.
-     *
+     * <p>
      * There is no determinate order of the elements in the stream.
-     *
+     * <p>
      * Returned streams are auto closable and should be used in a try-with-resources statement.
      *
      * @param selector the item selector
@@ -740,28 +885,34 @@ public class ManagedDefaultRepositoryContent
     {
         String projectId = selector.hasProjectId( ) ? selector.getProjectId( ) : ( selector.hasArtifactId( ) ? selector.getArtifactId( )
             : null );
-        final Predicate<StorageAsset> filter = getFileFilterFromSelector( selector );
-        if (projectId!=null && selector.hasVersion()) {
+        final Predicate<StorageAsset> filter = getArtifactFileFilterFromSelector( selector );
+        if ( projectId != null && selector.hasVersion( ) )
+        {
             return getAsset( selector.getNamespace( ), projectId, selector.getVersion( ) )
                 .list( ).stream( ).filter( filter )
                 .map( this::getArtifactFromPath );
-        } else if (projectId!=null) {
+        }
+        else if ( projectId != null )
+        {
             final StorageAsset projDir = getAsset( selector.getNamespace( ), projectId );
             return projDir.list( ).stream( )
-                .map(a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
+                .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
                 .flatMap( List::stream )
                 .filter( filter )
                 .map( this::getArtifactFromPath );
-        } else
+        }
+        else
         {
             StorageAsset namespaceDir = getAsset( selector.getNamespace( ) );
-            if (selector.recurse())
+            if ( selector.recurse( ) )
             {
                 return StorageUtil.newAssetStream( namespaceDir, true )
                     .filter( filter )
-                        .map( this::getArtifactFromPath );
+                    .map( this::getArtifactFromPath );
 
-            } else {
+            }
+            else
+            {
                 // We descend into 2 subdirectories (project and version)
                 return namespaceDir.list( ).stream( )
                     .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
@@ -783,13 +934,15 @@ public class ManagedDefaultRepositoryContent
     @Override
     public List<? extends Artifact> getArtifacts( ContentItem item )
     {
-        try(Stream<? extends Artifact> stream = newArtifactStream( item )) {
-            return stream.collect( Collectors.toList());
+        try ( Stream<? extends Artifact> stream = newArtifactStream( item ) )
+        {
+            return stream.collect( Collectors.toList( ) );
         }
     }
 
     /**
      * Returns all artifacts
+     *
      * @param item
      * @return
      * @throws ContentAccessException
@@ -801,20 +954,21 @@ public class ManagedDefaultRepositoryContent
 
     public Stream<? extends Artifact> newArtifactStream( Project item ) throws ContentAccessException
     {
-        return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ).getNamespace() )
-            .withProjectId( item.getId() ).build( ) );
+        return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ).getNamespace( ) )
+            .withProjectId( item.getId( ) ).build( ) );
     }
 
     public Stream<? extends Artifact> newArtifactStream( Version item ) throws ContentAccessException
     {
-        return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getProject().getNamespace( ).getNamespace() )
-            .withProjectId( item.getProject().getId() )
-            .withVersion( item.getVersion() ).build( ) );
+        return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getProject( ).getNamespace( ).getNamespace( ) )
+            .withProjectId( item.getProject( ).getId( ) )
+            .withVersion( item.getVersion( ) ).build( ) );
     }
 
     /**
      * Returns all related artifacts that match the given artifact. That means all artifacts that have
      * the same filename plus an additional extension, e.g. ${fileName}.sha2
+     *
      * @param item the artifact
      * @return the stream of artifacts
      * @throws ContentAccessException
@@ -828,6 +982,7 @@ public class ManagedDefaultRepositoryContent
         return v.getAsset( ).list( ).stream( ).filter( filter )
             .map( a -> getArtifactFromPath( a ) );
     }
+
     /**
      * Returns the stream of artifacts that are children of the given item.
      *
@@ -838,21 +993,162 @@ public class ManagedDefaultRepositoryContent
     @Override
     public Stream<? extends Artifact> newArtifactStream( ContentItem item ) throws ContentAccessException
     {
-        if (item instanceof Namespace) {
+        if ( item instanceof Namespace )
+        {
             return newArtifactStream( ( (Namespace) item ) );
-        } else if (item instanceof Project) {
+        }
+        else if ( item instanceof Project )
+        {
             return newArtifactStream( (Project) item );
-        } else if (item instanceof Version) {
+        }
+        else if ( item instanceof Version )
+        {
             return newArtifactStream( (Version) item );
-        } else if (item instanceof Artifact) {
+        }
+        else if ( item instanceof Artifact )
+        {
             return newArtifactStream( (Artifact) item );
-        } else
+        }
+        else
         {
             log.warn( "newArtifactStream for unsupported item requested: {}", item.getClass( ).getName( ) );
             return Stream.empty( );
         }
     }
 
+    private void appendPatternRegex( StringBuilder builder, String name )
+    {
+        String[] patternArray = name.split( "[*]" );
+        for ( int i = 0; i < patternArray.length - 1; i++ )
+        {
+            builder.append( Pattern.quote( patternArray[i] ) )
+                .append( "[A-Za-z0-9_\\-]*" );
+        }
+        builder.append( Pattern.quote( patternArray[patternArray.length - 1] ) );
+    }
+
+    Predicate<StorageAsset> getItemFileFilterFromSelector( ItemSelector selector )
+    {
+        if ( !selector.hasNamespace( ) && !selector.hasProjectId( ) )
+        {
+            throw new IllegalArgumentException( "Selector must have at least namespace and projectid" );
+        }
+        StringBuilder pathMatcher = new StringBuilder( "^" );
+        if ( selector.hasNamespace( ) )
+        {
+            String path = "/" + String.join( "/", selector.getNamespace( ).split( "\\." ) );
+            if ( path.contains( "*" ) )
+            {
+                appendPatternRegex( pathMatcher, path );
+            }
+            else
+            {
+                pathMatcher.append( Pattern.quote( path ) );
+            }
+
+        }
+        if ( selector.hasProjectId( ) )
+        {
+            pathMatcher.append( "/" );
+            if ( selector.getProjectId( ).contains( "*" ) )
+            {
+                appendPatternRegex( pathMatcher, selector.getProjectId( ) );
+            }
+            else
+            {
+                pathMatcher.append( Pattern.quote( selector.getProjectId( ) ) );
+            }
+        }
+        if ( selector.hasVersion( ) )
+        {
+            pathMatcher.append( "/" );
+            if ( selector.getVersion( ).contains( "*" ) )
+            {
+                appendPatternRegex( pathMatcher, selector.getVersion( ) );
+            }
+            else
+            {
+                pathMatcher.append( Pattern.quote( selector.getVersion( ) ) );
+            }
+        }
+        pathMatcher.append( ".*" );
+        final Pattern pathPattern = Pattern.compile( pathMatcher.toString( ) );
+        final Predicate<StorageAsset> pathPredicate = ( StorageAsset asset ) -> pathPattern.matcher( asset.getPath( ) ).matches( );
+        if ( selector.hasArtifactId( ) || selector.hasArtifactVersion( ) || selector.hasClassifier( )
+            || selector.hasType( ) || selector.hasExtension( ) )
+        {
+            return getArtifactFileFilterFromSelector( selector ).and( pathPredicate );
+        }
+        else
+        {
+            return pathPredicate;
+        }
+    }
+
+    /**
+     * Returns a concatenation of the asset and its children as stream, if they exist.
+     * It descends <code>level+1</code> levels down.
+     *
+     * @param a the asset to start from
+     * @param level the number of child levels to descend. 0 means only the children of the given asset, 1 means the children of childrens of the given asset, ...
+     * @return the stream of storage assets
+     */
+    private Stream<StorageAsset> getChildrenDF( StorageAsset a, int level )
+    {
+        if ( a.isContainer( ) )
+        {
+            if (level>0) {
+                return Stream.concat( a.list().stream( ).flatMap( ch -> getChildrenDF( ch, level - 1 ) ), Stream.of( a ) );
+            } else
+            {
+                return Stream.concat( a.list( ).stream( ), Stream.of( a ) );
+            }
+        }
+        else
+        {
+            return Stream.of( a );
+        }
+    }
+
+    @Override
+    public Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException
+    {
+        final Predicate<StorageAsset> filter = getItemFileFilterFromSelector( selector );
+        StorageAsset startDir;
+        if (selector.getNamespace().contains("*")) {
+            startDir = getAsset( "" );
+        } else if ( selector.hasProjectId( ) && selector.getProjectId().contains("*") )
+        {
+            startDir = getAsset( selector.getNamespace( ) );
+        } else if ( selector.hasProjectId() && selector.hasVersion() && selector.getVersion().contains("*")) {
+            startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
+        }
+        else if ( selector.hasProjectId( ) && selector.hasVersion( ) )
+        {
+            startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion() );
+        }
+        else if ( selector.hasProjectId( ) )
+        {
+            startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
+        }
+        else
+        {
+            startDir = getAsset( selector.getNamespace( ) );
+            if ( !selector.recurse( ) )
+            {
+                // We descend into 2 subdirectories (project and version)
+                return startDir.list( ).stream( )
+                    .flatMap( a -> getChildrenDF( a, 1 ) )
+                    .map( this::getItemFromPath );
+            }
+        }
+        ;
+        return StorageUtil.newAssetStream( startDir, parallel )
+            .filter( filter )
+            .map( this::getItemFromPath );
+
+    }
+
     /**
      * Checks, if the asset/file queried by the given selector exists.
      */
@@ -862,6 +1158,52 @@ public class ManagedDefaultRepositoryContent
         return getItem( selector ).getAsset( ).exists( );
     }
 
+    @Override
+    public ContentItem getParent( ContentItem item )
+    {
+        return getItemFromPath( item.getAsset( ).getParent( ) );
+    }
+
+    @Override
+    public List<? extends ContentItem> getChildren( ContentItem item )
+    {
+        if (item.getAsset().isLeaf()) {
+            return Collections.emptyList( );
+        } else {
+            return item.getAsset( ).list( ).stream( ).map( a -> getItemFromPath( a ) ).collect( Collectors.toList( ) );
+        }
+    }
+
+    @Override
+    public <T extends ContentItem> T applyCharacteristic( Class<T> clazz, ContentItem item ) throws LayoutException
+    {
+            if (item.getAsset().isLeaf()) {
+                if (clazz.isAssignableFrom( Artifact.class )) {
+                    Artifact artifact = getArtifactFromPath( item.getAsset( ) );
+                    item.setCharacteristic( Artifact.class, artifact );
+                    return (T) artifact;
+                } else {
+                    throw new LayoutException( "Could not adapt file to clazz " + clazz );
+                }
+            } else {
+                if (clazz.isAssignableFrom( Version.class )) {
+                    Version version = getVersionFromPath( item.getAsset( ) );
+                    item.setCharacteristic( Version.class, version );
+                    return (T) version;
+                } else if (clazz.isAssignableFrom( Project.class )) {
+                    Project project = getProjectFromPath( item.getAsset( ) );
+                    item.setCharacteristic( Project.class, project );
+                    return (T) project;
+                } else if (clazz.isAssignableFrom( Namespace.class )) {
+                    Namespace ns = getNamespaceFromPath( item.getAsset( ) );
+                    item.setCharacteristic( Namespace.class, ns );
+                    return (T) ns;
+                } else {
+                    throw new LayoutException( "Cannot adapt directory to clazz " + clazz );
+                }
+            }
+    }
+
     /**
      * Moves the file to the artifact destination
      */
@@ -871,14 +1213,15 @@ public class ManagedDefaultRepositoryContent
         try
         {
             StorageAsset asset = destination.getAsset( );
-            if (!asset.exists()) {
-                asset.create();
+            if ( !asset.exists( ) )
+            {
+                asset.create( );
             }
             asset.replaceDataFromFile( sourceFile );
         }
         catch ( IOException e )
         {
-            log.error( "Could not push data to asset source={} destination={}. {}", sourceFile, destination.getAsset().getFilePath(), e.getMessage( ) );
+            log.error( "Could not push data to asset source={} destination={}. {}", sourceFile, destination.getAsset( ).getFilePath( ), e.getMessage( ) );
             throw new ContentAccessException( e.getMessage( ), e );
         }
     }
@@ -887,11 +1230,13 @@ public class ManagedDefaultRepositoryContent
     public ContentItem toItem( String path ) throws LayoutException
     {
         StorageAsset asset = getRepository( ).getAsset( path );
-        if (asset.isLeaf())
+        if ( asset.isLeaf( ) )
         {
             ItemSelector selector = getPathParser( ).toItemSelector( path );
             return getItem( selector );
-        } else {
+        }
+        else
+        {
             return getItemFromPath( asset );
         }
     }
@@ -906,22 +1251,26 @@ public class ManagedDefaultRepositoryContent
 
     /**
      * Returns a version reference from the coordinates
-     * @param groupId the group id
+     *
+     * @param groupId    the group id
      * @param artifactId the artifact id
-     * @param version the version
+     * @param version    the version
      * @return the versioned reference object
      */
     @Override
-    public VersionedReference toVersion( String groupId, String artifactId, String version ) {
-        return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
+    public VersionedReference toVersion( String groupId, String artifactId, String version )
+    {
+        return new VersionedReference( ).groupId( groupId ).artifactId( artifactId ).version( version );
     }
 
     /**
      * Return the version the artifact is part of
+     *
      * @param artifactReference
      * @return
      */
-    public VersionedReference toVersion( ArtifactReference artifactReference) {
+    public VersionedReference toVersion( ArtifactReference artifactReference )
+    {
         return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
     }
 
@@ -930,13 +1279,13 @@ public class ManagedDefaultRepositoryContent
     public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
     {
         final String path = toPath( ref );
-        final Path deleteTarget = getRepoDir().resolve(path);
-        if ( !Files.exists(deleteTarget) )
+        final Path deleteTarget = getRepoDir( ).resolve( path );
+        if ( !Files.exists( deleteTarget ) )
         {
-            log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
+            log.warn( "Version path for repository {} does not exist: {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Version not found for repository " + getId( ) + ": " + path );
         }
-        if ( Files.isDirectory(deleteTarget) )
+        if ( Files.isDirectory( deleteTarget ) )
         {
             try
             {
@@ -945,11 +1294,13 @@ public class ManagedDefaultRepositoryContent
             catch ( IOException e )
             {
                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
-                throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
+                throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
             }
-        } else {
-            log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
+        }
+        else
+        {
+            log.warn( "Version path for repository {} is not a directory {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Version path for repository " + getId( ) + " is not directory: " + path );
         }
     }
 
@@ -959,12 +1310,12 @@ public class ManagedDefaultRepositoryContent
     {
         final String path = toPath( ref );
         final Path deleteTarget = getRepoDir( ).resolve( path );
-        if ( !Files.exists(deleteTarget) )
+        if ( !Files.exists( deleteTarget ) )
         {
-            log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
+            log.warn( "Project path for repository {} does not exist: {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Project not found for repository " + getId( ) + ": " + path );
         }
-        if ( Files.isDirectory(deleteTarget) )
+        if ( Files.isDirectory( deleteTarget ) )
         {
             try
             {
@@ -973,13 +1324,13 @@ public class ManagedDefaultRepositoryContent
             catch ( IOException e )
             {
                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
-                throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
+                throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
             }
         }
         else
         {
-            log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
+            log.warn( "Project path for repository {} is not a directory {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Project path for repository " + getId( ) + " is not directory: " + path );
         }
 
     }
@@ -987,7 +1338,7 @@ public class ManagedDefaultRepositoryContent
     @Override
     public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
     {
-        this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
+        this.deleteProject( new ProjectReference( ).groupId( namespace ).artifactId( projectId ) );
     }
 
     @Override
@@ -996,25 +1347,29 @@ public class ManagedDefaultRepositoryContent
         final String path = toPath( ref );
         final Path repoDir = getRepoDir( );
         Path deleteTarget = repoDir.resolve( path );
-        if ( Files.exists(deleteTarget) )
+        if ( Files.exists( deleteTarget ) )
         {
             try
             {
-                if (Files.isDirectory( deleteTarget ))
+                if ( Files.isDirectory( deleteTarget ) )
                 {
                     org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
-                } else {
+                }
+                else
+                {
                     Files.delete( deleteTarget );
                 }
             }
             catch ( IOException e )
             {
                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
-                throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
+                throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
             }
-        } else {
-            log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
+        }
+        else
+        {
+            log.warn( "Artifact path for repository {} does not exist: {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Artifact not found for repository " + getId( ) + ": " + path );
         }
 
     }
@@ -1025,11 +1380,12 @@ public class ManagedDefaultRepositoryContent
     {
         final String path = toPath( groupId );
         final Path deleteTarget = getRepoDir( ).resolve( path );
-        if (!Files.exists(deleteTarget)) {
-            log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
+        if ( !Files.exists( deleteTarget ) )
+        {
+            log.warn( "Namespace path for repository {} does not exist: {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Namespace not found for repository " + getId( ) + ": " + path );
         }
-        if ( Files.isDirectory(deleteTarget) )
+        if ( Files.isDirectory( deleteTarget ) )
         {
             try
             {
@@ -1038,19 +1394,21 @@ public class ManagedDefaultRepositoryContent
             catch ( IOException e )
             {
                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
-                throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
+                throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
             }
-        } else {
-            log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
-            throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
+        }
+        else
+        {
+            log.warn( "Namespace path for repository {} is not a directory {}", getId( ), deleteTarget );
+            throw new ContentNotFoundException( "Namespace path for repository " + getId( ) + " is not directory: " + path );
 
         }
     }
 
     @Override
-    public String getId()
+    public String getId( )
     {
-        return repository.getId();
+        return repository.getId( );
     }
 
     @Override
@@ -1058,45 +1416,59 @@ public class ManagedDefaultRepositoryContent
         throws ContentNotFoundException, LayoutException, ContentAccessException
     {
         StorageAsset artifactDir = toFile( reference );
-        if ( !artifactDir.exists())
+        if ( !artifactDir.exists( ) )
         {
             throw new ContentNotFoundException(
-                "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
+                "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath( ) );
         }
 
-        if ( !artifactDir.isContainer() )
+        if ( !artifactDir.isContainer( ) )
         {
             throw new ContentNotFoundException(
-                "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
+                "Unable to get related artifacts using a non-directory: " + artifactDir.getPath( ) );
         }
 
         // First gather up the versions found as artifacts in the managed repository.
 
-        try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
-            return stream.filter(asset -> !asset.isContainer()).map(path -> {
-                try {
-                    ArtifactReference artifact = toArtifactReference(path.getPath());
-                    if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
-                            reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
+        try ( Stream<? extends StorageAsset> stream = artifactDir.list( ).stream( ) )
+        {
+            return stream.filter( asset -> !asset.isContainer( ) ).map( path -> {
+                try
+                {
+                    ArtifactReference artifact = toArtifactReference( path.getPath( ) );
+                    if ( artifact.getGroupId( ).equals( reference.getGroupId( ) ) && artifact.getArtifactId( ).equals(
+                        reference.getArtifactId( ) ) && artifact.getVersion( ).equals( reference.getVersion( ) ) )
+                    {
                         return artifact;
-                    } else {
+                    }
+                    else
+                    {
                         return null;
                     }
-                } catch (LayoutException e) {
-                    log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
+                }
+                catch ( LayoutException e )
+                {
+                    log.debug( "Not processing file that is not an artifact: {}", e.getMessage( ) );
                     return null;
                 }
-            }).filter(Objects::nonNull).collect(Collectors.toList());
-        } catch (RuntimeException e) {
+            } ).filter( Objects::nonNull ).collect( Collectors.toList( ) );
+        }
+        catch ( RuntimeException e )
+        {
             Throwable cause = e.getCause( );
-            if (cause!=null) {
-                if (cause instanceof LayoutException) {
-                    throw (LayoutException)cause;
-                } else
+            if ( cause != null )
+            {
+                if ( cause instanceof LayoutException )
+                {
+                    throw (LayoutException) cause;
+                }
+                else
                 {
                     throw new ContentAccessException( cause.getMessage( ), cause );
                 }
-            } else {
+            }
+            else
+            {
                 throw new ContentAccessException( e.getMessage( ), e );
             }
         }
@@ -1105,41 +1477,49 @@ public class ManagedDefaultRepositoryContent
     /*
      * Create the filter for various combinations of classifier and type
      */
-    private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
+    private Predicate<ArtifactReference> getChecker( ArtifactReference referenceObject, String extension )
+    {
         // TODO: Check, if extension is the correct parameter here
         // We compare type with extension which works for artifacts like .jar.md5 but may
         // be not the best way.
 
-        if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
-            return ((ArtifactReference a) ->
-                referenceObject.getGroupId().equals( a.getGroupId() )
-                && referenceObject.getArtifactId().equals( a.getArtifactId() )
-                && referenceObject.getVersion( ).equals( a.getVersion( ) )
-                && ( (a.getType()==null)
-                    || referenceObject.getType().equals( a.getType() )
-                    || a.getType().startsWith(extension) )
-                && referenceObject.getClassifier().equals( a.getClassifier() )
+        if ( referenceObject.getClassifier( ) != null && referenceObject.getType( ) != null )
+        {
+            return ( ( ArtifactReference a ) ->
+                referenceObject.getGroupId( ).equals( a.getGroupId( ) )
+                    && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
+                    && referenceObject.getVersion( ).equals( a.getVersion( ) )
+                    && ( ( a.getType( ) == null )
+                    || referenceObject.getType( ).equals( a.getType( ) )
+                    || a.getType( ).startsWith( extension ) )
+                    && referenceObject.getClassifier( ).equals( a.getClassifier( ) )
             );
-        } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
-            return ((ArtifactReference a) ->
-                referenceObject.getGroupId().equals( a.getGroupId() )
-                    && referenceObject.getArtifactId().equals( a.getArtifactId() )
+        }
+        else if ( referenceObject.getClassifier( ) != null && referenceObject.getType( ) == null )
+        {
+            return ( ( ArtifactReference a ) ->
+                referenceObject.getGroupId( ).equals( a.getGroupId( ) )
+                    && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
-                    && referenceObject.getClassifier().equals( a.getClassifier() )
+                    && referenceObject.getClassifier( ).equals( a.getClassifier( ) )
             );
-        } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
-            return ((ArtifactReference a) ->
-                referenceObject.getGroupId().equals( a.getGroupId() )
-                    && referenceObject.getArtifactId().equals( a.getArtifactId() )
+        }
+        else if ( referenceObject.getClassifier( ) == null && referenceObject.getType( ) != null )
+        {
+            return ( ( ArtifactReference a ) ->
+                referenceObject.getGroupId( ).equals( a.getGroupId( ) )
+                    && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
-                    && ( (a.getType()==null)
-                    || referenceObject.getType().equals( a.getType() )
-                    || a.getType().startsWith(extension) )
+                    && ( ( a.getType( ) == null )
+                    || referenceObject.getType( ).equals( a.getType( ) )
+                    || a.getType( ).startsWith( extension ) )
             );
-        } else {
-            return ((ArtifactReference a) ->
-                referenceObject.getGroupId().equals( a.getGroupId() )
-                    && referenceObject.getArtifactId().equals( a.getArtifactId() )
+        }
+        else
+        {
+            return ( ( ArtifactReference a ) ->
+                referenceObject.getGroupId( ).equals( a.getGroupId( ) )
+                    && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
             );
         }
@@ -1148,23 +1528,29 @@ public class ManagedDefaultRepositoryContent
     }
 
     @Override
-    public String getRepoRoot()
+    public String getRepoRoot( )
     {
-        return convertUriToPath( repository.getLocation() );
+        return convertUriToPath( repository.getLocation( ) );
     }
 
-    private String convertUriToPath( URI uri ) {
-        if (uri.getScheme()==null) {
-            return Paths.get(uri.getPath()).toString();
-        } else if ("file".equals(uri.getScheme())) {
-            return Paths.get(uri).toString();
-        } else {
-            return uri.toString();
+    private String convertUriToPath( URI uri )
+    {
+        if ( uri.getScheme( ) == null )
+        {
+            return Paths.get( uri.getPath( ) ).toString( );
+        }
+        else if ( "file".equals( uri.getScheme( ) ) )
+        {
+            return Paths.get( uri ).toString( );
+        }
+        else
+        {
+            return uri.toString( );
         }
     }
 
     @Override
-    public ManagedRepository getRepository()
+    public ManagedRepository getRepository( )
     {
         return repository;
     }
@@ -1173,7 +1559,7 @@ public class ManagedDefaultRepositoryContent
     public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
     {
         StorageAsset artifactFile = toFile( reference );
-        return artifactFile.exists() && !artifactFile.isContainer();
+        return artifactFile.exists( ) && !artifactFile.isContainer( );
     }
 
     @Override
@@ -1190,7 +1576,7 @@ public class ManagedDefaultRepositoryContent
         catch ( IOException e )
         {
             String path = toPath( reference );
-            log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
+            log.error( "Could not read directory from repository {} - {}: ", getId( ), path, e.getMessage( ), e );
             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
         }
     }
@@ -1199,18 +1585,22 @@ public class ManagedDefaultRepositoryContent
     public void setRepository( final ManagedRepository repo )
     {
         this.repository = repo;
-        if (repo!=null) {
-            if (repository instanceof EditableManagedRepository) {
-                ((EditableManagedRepository) repository).setContent(this);
+        if ( repo != null )
+        {
+            if ( repository instanceof EditableManagedRepository )
+            {
+                ( (EditableManagedRepository) repository ).setContent( this );
             }
         }
     }
 
-    private Path getRepoDir() {
+    private Path getRepoDir( )
+    {
         return repository.getAsset( "" ).getFilePath( );
     }
 
-    private RepositoryStorage getStorage() {
+    private RepositoryStorage getStorage( )
+    {
         return repository.getAsset( "" ).getStorage( );
     }
 
@@ -1224,15 +1614,19 @@ public class ManagedDefaultRepositoryContent
     public ArtifactReference toArtifactReference( String path )
         throws LayoutException
     {
-        String repoPath = convertUriToPath( repository.getLocation() );
-        if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
+        String repoPath = convertUriToPath( repository.getLocation( ) );
+        if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length( ) > 0 )
+        {
+            return super.toArtifactReference( path.substring( repoPath.length( ) + 1 ) );
+        }
+        else
         {
-            return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
-        } else {
             repoPath = path;
-            if (repoPath!=null) {
-                while (repoPath.startsWith("/")) {
-                    repoPath = repoPath.substring(1);
+            if ( repoPath != null )
+            {
+                while ( repoPath.startsWith( "/" ) )
+                {
+                    repoPath = repoPath.substring( 1 );
                 }
             }
             return super.toArtifactReference( repoPath );
@@ -1241,20 +1635,23 @@ public class ManagedDefaultRepositoryContent
 
 
     // The variant with runtime exception for stream usage
-    private ArtifactReference toArtifactRef(String path) {
-        try {
-            return toArtifactReference(path);
-        } catch (LayoutException e) {
-            throw new RuntimeException(e);
+    private ArtifactReference toArtifactRef( String path )
+    {
+        try
+        {
+            return toArtifactReference( path );
+        }
+        catch ( LayoutException e )
+        {
+            throw new RuntimeException( e );
         }
     }
 
 
-
     @Override
     public StorageAsset toFile( ArtifactReference reference )
     {
-        return repository.getAsset(toPath(reference));
+        return repository.getAsset( toPath( reference ) );
     }
 
     @Override
@@ -1274,52 +1671,56 @@ public class ManagedDefaultRepositoryContent
      *
      * @param reference the reference to the versioned reference to search within
      * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
-     *         no artifact was found within the versioned reference.
-     * @throws java.io.IOException     if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
+     * no artifact was found within the versioned reference.
+     * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
      * @throws LayoutException
      */
     private ArtifactReference getFirstArtifact( VersionedReference reference )
         throws ContentNotFoundException, LayoutException, IOException
     {
-        try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
+        try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
         {
             return stream.findFirst( ).orElse( null );
-        } catch (RuntimeException e) {
+        }
+        catch ( RuntimeException e )
+        {
             throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
         }
     }
 
-    private Stream<ArtifactReference> newArtifactStream( VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
+    private Stream<ArtifactReference> newArtifactStream( VersionedReference reference ) throws ContentNotFoundException, LayoutException, IOException
+    {
         final Path repoBase = getRepoDir( );
         String path = toMetadataPath( reference );
-        Path versionDir = repoBase.resolve( path ).getParent();
-        if ( !Files.exists(versionDir) )
+        Path versionDir = repoBase.resolve( path ).getParent( );
+        if ( !Files.exists( versionDir ) )
         {
             throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
-                + versionDir.toAbsolutePath() );
+                + versionDir.toAbsolutePath( ) );
         }
 
-        if ( !Files.isDirectory(versionDir) )
+        if ( !Files.isDirectory( versionDir ) )
         {
             throw new ContentNotFoundException(
-                "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
+                "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath( ) );
         }
-        return Files.list(versionDir).filter(Files::isRegularFile)
-                .map(p -> repoBase.relativize(p).toString())
-                .filter(p -> !filetypes.matchesDefaultExclusions(p))
-                .filter(filetypes::matchesArtifactPattern)
-                .map(this::toArtifactRef);
+        return Files.list( versionDir ).filter( Files::isRegularFile )
+            .map( p -> repoBase.relativize( p ).toString( ) )
+            .filter( p -> !filetypes.matchesDefaultExclusions( p ) )
+            .filter( filetypes::matchesArtifactPattern )
+            .map( this::toArtifactRef );
     }
 
-    public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
+    public List<ArtifactReference> getArtifacts( VersionedReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
     {
-        try (Stream<ArtifactReference> stream = newArtifactStream( reference ))
+        try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
         {
             return stream.collect( Collectors.toList( ) );
-        } catch ( IOException e )
+        }
+        catch ( IOException e )
         {
             String path = toPath( reference );
-            log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
+            log.error( "Could not read directory from repository {} - {}: ", getId( ), path, e.getMessage( ), e );
             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
 
         }
@@ -1328,14 +1729,18 @@ public class ManagedDefaultRepositoryContent
     private boolean hasArtifact( VersionedReference reference )
 
     {
-        try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
+        try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
         {
             return stream.anyMatch( e -> true );
-        } catch (ContentNotFoundException e) {
+        }
+        catch ( ContentNotFoundException e )
+        {
             return false;
-        } catch ( LayoutException | IOException e) {
+        }
+        catch ( LayoutException | IOException e )
+        {
             // We throw the runtime exception for better stream handling
-            throw new RuntimeException(e);
+            throw new RuntimeException( e );
         }
     }
 
@@ -1344,9 +1749,19 @@ public class ManagedDefaultRepositoryContent
         this.filetypes = filetypes;
     }
 
-    public void setMavenContentHelper( MavenContentHelper contentHelper) {
+    public void setMavenContentHelper( MavenContentHelper contentHelper )
+    {
         this.mavenContentHelper = contentHelper;
     }
 
 
+    public MavenMetadataReader getMetadataReader( )
+    {
+        return metadataReader;
+    }
+
+    public void setMetadataReader( MavenMetadataReader metadataReader )
+    {
+        this.metadataReader = metadataReader;
+    }
 }
index 05e45f99a23752d76dbd850b87e9f9d350c6207d..03bcb02820322073c28ef2b2c2aa63ed097c2732 100644 (file)
@@ -23,6 +23,7 @@ import org.apache.archiva.common.utils.VersionComparator;
 import org.apache.archiva.configuration.ArchivaConfiguration;
 import org.apache.archiva.configuration.FileType;
 import org.apache.archiva.configuration.FileTypes;
+import org.apache.archiva.metadata.maven.MavenMetadataReader;
 import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.model.ProjectReference;
 import org.apache.archiva.model.VersionedReference;
@@ -34,17 +35,20 @@ import org.apache.archiva.repository.RepositoryContent;
 import org.apache.archiva.repository.content.Artifact;
 import org.apache.archiva.repository.content.BaseArtifactTypes;
 import org.apache.archiva.repository.content.ContentItem;
+import org.apache.archiva.repository.content.DataItem;
 import org.apache.archiva.repository.content.ItemNotFoundException;
 import org.apache.archiva.repository.content.ItemSelector;
 import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.Version;
+import org.apache.archiva.repository.content.base.ArchivaContentItem;
+import org.apache.archiva.repository.content.base.ArchivaDataItem;
 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
 import org.apache.archiva.repository.maven.MavenManagedRepository;
 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
+import org.apache.archiva.repository.metadata.MetadataReader;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -59,11 +63,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.FileTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -90,6 +90,10 @@ public class ManagedDefaultRepositoryContentTest
     @Inject
     MavenContentHelper contentHelper;
 
+    @Inject
+    @Named( "metadataReader#maven" )
+    MavenMetadataReader metadataReader;
+
     @Inject
     FileLockManager fileLockManager;
 
@@ -120,6 +124,7 @@ public class ManagedDefaultRepositoryContentTest
 
         repoContent = new ManagedDefaultRepositoryContent(repository, artifactMappingProviders, fileTypes, fileLockManager);
         repoContent.setMavenContentHelper( contentHelper );
+        repoContent.setMetadataReader( metadataReader );
         
         //repoContent = (ManagedRepositoryContent) lookup( ManagedRepositoryContent.class, "default" );
     }
@@ -128,7 +133,7 @@ public class ManagedDefaultRepositoryContentTest
     public void testGetVersionsSnapshotA()
         throws Exception
     {
-        assertVersions( "snap_shots_a", "1.0-alpha-11-SNAPSHOT",
+        assertArtifactVersions( "snap_shots_a", "1.0-alpha-11-SNAPSHOT",
                         new String[]{ "1.0-alpha-11-SNAPSHOT", "1.0-alpha-11-20070221.194724-2",
                             "1.0-alpha-11-20070302.212723-3", "1.0-alpha-11-20070303.152828-4",
                             "1.0-alpha-11-20070305.215149-5", "1.0-alpha-11-20070307.170909-6",
@@ -203,6 +208,31 @@ public class ManagedDefaultRepositoryContentTest
         assertArrayEquals( expectedVersions, versions.toArray( ) );
 
 
+    }
+
+    private void assertArtifactVersions( String artifactId, String version, String[] expectedVersions )
+        throws Exception
+    {
+        // Use the test metadata-repository, which is already setup for
+        // These kind of version tests.
+        Path repoDir = getRepositoryPath( "metadata-repository" );
+        ((EditableManagedRepository)repoContent.getRepository()).setLocation( repoDir.toAbsolutePath().toUri() );
+
+        // Request the versions.
+
+        // Sort the list (for asserts later)
+        final VersionComparator comparator = new VersionComparator( );
+
+        ItemSelector selector = ArchivaItemSelector.builder( )
+            .withNamespace( "org.apache.archiva.metadata.tests" )
+            .withProjectId( artifactId )
+            .withVersion( version )
+            .build( );
+        List<String> versions = repoContent.getArtifactVersions( selector ).stream()
+            .sorted( comparator ).collect( Collectors.toList());
+        assertArrayEquals( expectedVersions, versions.toArray( ) );
+
+
     }
 
     @Test
@@ -558,10 +588,10 @@ public class ManagedDefaultRepositoryContentTest
         assertEquals( 2, results.size( ) );
         Artifact mainArtifact = results.stream( ).filter( a -> a.getFileName( ).equals( "jdbc-2.0.jar" ) ).findFirst( ).get( );
         assertNotNull( mainArtifact );
-        assertEquals( BaseArtifactTypes.MAIN, mainArtifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, mainArtifact.getDataType( ) );
         Artifact metaArtifact = results.stream( ).filter( a -> a.getFileName( ).equals( "maven-metadata-repository.xml" ) ).findFirst( ).get( );
         assertNotNull( metaArtifact );
-        assertEquals( MavenTypes.REPOSITORY_METADATA, metaArtifact.getArtifactType( ) );
+        assertEquals( MavenTypes.REPOSITORY_METADATA, metaArtifact.getDataType( ) );
     }
 
     @Test
@@ -598,7 +628,7 @@ public class ManagedDefaultRepositoryContentTest
 
         assertNotNull( artifact );
         assertEquals( "pom", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.MAIN, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, artifact.getDataType( ) );
         assertEquals( "1.3-SNAPSHOT", artifact.getVersion( ).getVersion( ) );
         assertEquals( "1.3-20070725.210059-1", artifact.getArtifactVersion( ) );
         assertEquals( ".pom", artifact.getRemainder( ) );
@@ -614,7 +644,7 @@ public class ManagedDefaultRepositoryContentTest
 
         assertNotNull( artifact );
         assertEquals( "md5", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.RELATED, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.RELATED, artifact.getDataType( ) );
         assertEquals( "1.3-SNAPSHOT", artifact.getVersion( ).getVersion( ) );
         assertEquals( "1.3-20070725.210059-1", artifact.getArtifactVersion( ) );
         assertEquals( ".pom.md5", artifact.getRemainder( ) );
@@ -629,7 +659,7 @@ public class ManagedDefaultRepositoryContentTest
         artifact = results.stream( ).filter( a -> a.getFileName( ).equals( "maven-metadata.xml" ) )
             .findFirst( ).get( );
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.METADATA, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.METADATA, artifact.getDataType( ) );
         assertEquals( "1.3-SNAPSHOT", artifact.getVersion( ).getVersion( ) );
         assertEquals( "xml", artifact.getExtension( ) );
     }
@@ -668,7 +698,7 @@ public class ManagedDefaultRepositoryContentTest
         assertEquals( 1, results.size( ) );
         Artifact artifact = results.get( 0 );
         assertEquals( "pom", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.MAIN, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, artifact.getDataType( ) );
     }
 
     @Test
@@ -711,13 +741,13 @@ public class ManagedDefaultRepositoryContentTest
             .findFirst( ).get( );
         assertNotNull( artifact );
         assertEquals( "pom", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.MAIN, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, artifact.getDataType( ) );
 
         artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "axis2-1.3-20070731.113304-21.pom.sha1" ) )
             .findFirst( ).get( );
         assertNotNull( artifact );
         assertEquals( "sha1", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.RELATED, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.RELATED, artifact.getDataType( ) );
     }
 
 
@@ -752,19 +782,19 @@ public class ManagedDefaultRepositoryContentTest
             .findFirst( ).get( );
         assertNotNull( artifact );
         assertEquals( "xml", artifact.getExtension( ) );
-        assertEquals( BaseArtifactTypes.METADATA, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.METADATA, artifact.getDataType( ) );
 
         artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "maven-downloader-1.0-sources.jar" ) )
             .findFirst( ).get( );
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.MAIN, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, artifact.getDataType( ) );
         assertEquals( "sources", artifact.getClassifier( ) );
         assertEquals( "java-source", artifact.getType( ) );
 
         artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "maven-downloader-1.0-sources.jar.sha1" ) )
             .findFirst( ).get( );
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.RELATED, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.RELATED, artifact.getDataType( ) );
         assertEquals( "sources", artifact.getClassifier( ) );
         assertEquals( "sha1", artifact.getType( ) );
         assertEquals( ".jar.sha1", artifact.getRemainder( ) );
@@ -994,6 +1024,59 @@ public class ManagedDefaultRepositoryContentTest
         assertNotNull( artifact );
     }
 
+
+    @Test
+    public void testNewItemStreamWithNamespace1() {
+        ItemSelector selector = ArchivaItemSelector.builder( )
+            .withNamespace( "org.apache.axis2" )
+            .build();
+
+        Stream<? extends ContentItem> stream = repoContent.newItemStream( selector, false );
+        List<? extends ContentItem> result = stream.collect( Collectors.toList( ) );
+        assertEquals( 41, result.size( ) );
+        ContentItem item = result.get( 39 );
+        Version version = item.adapt( Version.class );
+        assertNotNull( version );
+        assertEquals( "1.3-SNAPSHOT", version.getVersion( ) );
+        Project project = result.get( 40 ).adapt( Project.class );
+        assertNotNull( project );
+        assertEquals( "axis2", project.getId( ) );
+        assertTrue( result.stream( ).anyMatch( a -> "axis2-1.3-20070725.210059-1.pom".equals( a.getAsset( ).getName( ) ) ) );
+    }
+
+//    @Test
+//    public void testNewItemStreamWithNamespace2() {
+//        ItemSelector selector = ArchivaItemSelector.builder( )
+//            .withNamespace( "org.apache.maven" )
+//            .build();
+//
+//        Stream<? extends ContentItem> stream = repoContent.newItemStream( selector, false );
+//        List<? extends ContentItem> result = stream.collect( Collectors.toList( ) );
+//        int versions = 0;
+//        int projects = 0;
+//        int artifacts = 0;
+//        int namespaces = 0;
+//        int dataitems = 0;
+//        for (int i=0; i<result.size(); i++) {
+//            ContentItem ci = result.get( i );
+//            System.out.println( i + ": " + result.get( i ) + " - " +result.get(i).getClass().getName() + " - " + result.get(i).getAsset().getPath() );
+//            if (ci instanceof Version) {
+//                versions++;
+//            } else if (ci instanceof Project) {
+//                projects++;
+//            } else if (ci instanceof Namespace) {
+//                namespaces++;
+//            } else if (ci instanceof Artifact) {
+//                artifacts++;
+//            } else if (ci instanceof DataItem ) {
+//                dataitems++;
+//            }
+//        }
+//        System.out.println( "namespaces=" + namespaces + ", projects=" + projects + ", versions=" + versions + ", artifacts=" + artifacts + ", dataitems=" + dataitems );
+//        assertEquals( 170, result.size( ) );
+//        assertEquals( 92, result.stream( ).filter( a -> a instanceof Artifact ).count( ) );
+//    }
+
     @Test
     public void testGetArtifactFromContentItem() {
         ItemSelector selector = ArchivaItemSelector.builder( )
@@ -1053,17 +1136,17 @@ public class ManagedDefaultRepositoryContentTest
         String path = "/org/apache/maven/shared";
         ContentItem item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Namespace );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = "/org/apache/maven/shared/maven-downloader";
         item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Project );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = "/org/apache/maven/shared/maven-downloader/1.1";
         item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Version );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = "/org/apache/maven/shared/maven-downloader/1.1/maven-downloader-1.1.jar";
         item = repoContent.toItem( path );
@@ -1078,22 +1161,22 @@ public class ManagedDefaultRepositoryContentTest
         StorageAsset path = repoContent.getRepository().getAsset("/org/apache/maven/shared");
         ContentItem item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Namespace );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = repoContent.getRepository( ).getAsset( "/org/apache/maven/shared/maven-downloader" );
         item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Project );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = repoContent.getRepository( ).getAsset( "/org/apache/maven/shared/maven-downloader/1.1" );
         item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Version );
+        assertTrue( item instanceof ArchivaContentItem );
 
         path = repoContent.getRepository( ).getAsset( "/org/apache/maven/shared/maven-downloader/1.1/maven-downloader-1.1.jar" );
         item = repoContent.toItem( path );
         assertNotNull( item );
-        assertTrue( item instanceof Artifact );
+        assertTrue( item instanceof DataItem );
 
     }
 
@@ -1197,18 +1280,18 @@ public class ManagedDefaultRepositoryContentTest
         Artifact artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "samplejar-1.0.jar" ) )
             .findFirst().get();
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.MAIN, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.MAIN, artifact.getDataType( ) );
 
         artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "samplejar-1.0.jar.md5" ) )
             .findFirst().get();
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.RELATED, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.RELATED, artifact.getDataType( ) );
         assertEquals( "md5", artifact.getExtension( ) );
 
         artifact = results.stream( ).filter( a -> a.getFileName( ).equalsIgnoreCase( "samplejar-1.0.jar.sha1" ) )
             .findFirst().get();
         assertNotNull( artifact );
-        assertEquals( BaseArtifactTypes.RELATED, artifact.getArtifactType( ) );
+        assertEquals( BaseArtifactTypes.RELATED, artifact.getDataType( ) );
         assertEquals( "sha1", artifact.getExtension( ) );
 
     }
@@ -1546,4 +1629,5 @@ public class ManagedDefaultRepositoryContentTest
         assertTrue( new String( content ).startsWith( "test.test.test" ) );
     }
 
+
 }
index 8d0a77b08c298c119025e1f9325308415cd6a464..6c0f7c1242cb7641d2669156772f650b01b39d64 100644 (file)
@@ -52,6 +52,7 @@ import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.content.ItemNotFoundException;
+import org.apache.archiva.repository.content.Version;
 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
 import org.apache.archiva.repository.storage.fs.FsStorageUtil;
 import org.apache.archiva.repository.storage.RepositoryStorage;
@@ -663,28 +664,19 @@ public class DefaultRepositoriesService
         {
             ManagedRepositoryContent repository = getManagedRepositoryContent( repositoryId );
 
-            VersionedReference ref = new VersionedReference();
-            ref.setArtifactId( projectId );
-            ref.setGroupId( namespace );
-            ref.setVersion( version );
-
-            repository.deleteVersion( ref );
-
 
-            ArtifactReference artifactReference = new ArtifactReference();
-            artifactReference.setGroupId( namespace );
-            artifactReference.setArtifactId( projectId );
-            artifactReference.setVersion( version );
+            ArchivaItemSelector selector = ArchivaItemSelector.builder( )
+                .withNamespace( namespace )
+                .withProjectId( projectId )
+                .withVersion( version )
+                .build( );
+            Version versionItem = repository.getVersion( selector );
+            if (versionItem!=null && versionItem.exists()) {
+                repository.deleteItem( versionItem );
+            }
 
             MetadataRepository metadataRepository = repositorySession.getRepository();
 
-            List<ArtifactReference> related = repository.getRelatedArtifacts( repository.toVersion(artifactReference) );
-            log.debug( "related: {}", related );
-            for ( ArtifactReference artifactRef : related )
-            {
-                repository.deleteArtifact( artifactRef );
-            }
-
             Collection<ArtifactMetadata> artifacts =
                 metadataRepository.getArtifacts(repositorySession , repositoryId, namespace, projectId, version );
 
@@ -695,7 +687,7 @@ public class DefaultRepositoriesService
 
             metadataRepository.removeProjectVersion(repositorySession , repositoryId, namespace, projectId, version );
         }
-        catch ( MetadataRepositoryException | MetadataResolutionException | RepositoryException | LayoutException e )
+        catch ( MetadataRepositoryException | MetadataResolutionException | RepositoryException | ItemNotFoundException e )
         {
             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
         }