diff options
author | Martin Stockhammer <martin_s@apache.org> | 2020-05-24 18:18:37 +0200 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2020-05-30 20:00:22 +0200 |
commit | ac25c7a86fe77f5b0f005f03d9d28dc1f6f8580e (patch) | |
tree | d6f1650edea83a7ed04c1ae40cf8e03d70ff8ed0 | |
parent | 56de9e590b8d040add03b99df2e221eeedde860e (diff) | |
download | archiva-ac25c7a86fe77f5b0f005f03d9d28dc1f6f8580e.tar.gz archiva-ac25c7a86fe77f5b0f005f03d9d28dc1f6f8580e.zip |
Changing content item API
21 files changed, 1584 insertions, 714 deletions
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 index 000000000..7242f5da9 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/GenericManagedRepositoryContent.java @@ -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 index 000000000..2290111f2 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ItemConversionException.java @@ -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 ); + } +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java index a093c8a6d..9b2b318b9 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java @@ -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) @@ -289,14 +243,6 @@ public interface ManagedRepositoryContent extends RepositoryContent /** - * 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 * * @param sourceFile the path to the source file @@ -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 ********************** diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/ContentItem.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/ContentItem.java index 8efdd8461..68aca2d8b 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/ContentItem.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/ContentItem.java @@ -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 diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/DataItem.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/DataItem.java index 32dc00ee5..5d315b54a 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/DataItem.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/DataItem.java @@ -29,12 +29,6 @@ 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 */ diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaArtifact.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaArtifact.java index 26b0449ba..b65fa7400 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaArtifact.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaArtifact.java @@ -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; diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaContentItem.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaContentItem.java index 7e15d61be..acba93b4f 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaContentItem.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaContentItem.java @@ -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( ); - } + } diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaDataItem.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaDataItem.java index cb13e1832..c825bb316 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaDataItem.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaDataItem.java @@ -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; @@ -68,12 +65,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem } @Override - public ContentItem getParent( ) - { - return parent; - } - - @Override public String getContentType( ) { return contentType; @@ -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( '}' ); @@ -168,18 +156,6 @@ public class ArchivaDataItem extends ArchivaContentItem implements DataItem } @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 ) { if ( StringUtils.isEmpty( id ) ) diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaNamespace.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaNamespace.java index b85f25a60..9982ea0de 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaNamespace.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaNamespace.java @@ -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 ); } diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaProject.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaProject.java index ac72f6217..9fb9a0cf6 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaProject.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaProject.java @@ -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()+"}"; } /* diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaVersion.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaVersion.java index e10d3b1c5..c553c3bfc 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaVersion.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ArchivaVersion.java @@ -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 index 000000000..3866131ec --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/BaseContentItem.java @@ -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 index 000000000..dd3a53db7 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/ContentItemBuilder.java @@ -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 index 000000000..36ca84375 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ArchivaContentItemOptBuilder.java @@ -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(); +} diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/DataItemOptBuilder.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/DataItemOptBuilder.java index 04da17d13..445946ec2 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/DataItemOptBuilder.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/DataItemOptBuilder.java @@ -32,8 +32,6 @@ public interface DataItemOptBuilder extends OptBuilder<ArchivaDataItem, DataItemOptBuilder> { - DataItemOptBuilder withParent( ContentItem parent ); - DataItemOptBuilder withContentType( String contentType ); DataItemOptBuilder withDataType( DataItemType type ); diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java index dfd770380..2e7871d00 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java @@ -164,6 +164,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @Override + public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException + { + return null; + } + + @Override public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException { return null; @@ -182,6 +188,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @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 { diff --git a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java index 7bbf51736..8a9442a36 100644 --- a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java +++ b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java @@ -164,6 +164,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @Override + public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException + { + return null; + } + + @Override public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException { return null; @@ -182,6 +188,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @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 { diff --git a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java index 6d3d60a0f..fea65d10f 100644 --- a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java +++ b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java @@ -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; @@ -83,6 +84,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @Override + public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException + { + + } + + @Override public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException { @@ -131,6 +138,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @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 { return null; @@ -155,6 +168,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @Override + public List<String> getArtifactVersions( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException + { + return null; + } + + @Override public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException { return null; @@ -173,6 +192,24 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent } @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 { diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java index af42632d6..51e3018e3 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java @@ -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; + } } diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java index 05e45f99a..03bcb0282 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java @@ -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; @@ -91,6 +91,10 @@ public class ManagedDefaultRepositoryContentTest MavenContentHelper contentHelper; @Inject + @Named( "metadataReader#maven" ) + MavenMetadataReader metadataReader; + + @Inject FileLockManager fileLockManager; private Path getRepositoryPath(String repoName) { @@ -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", @@ -205,6 +210,31 @@ public class ManagedDefaultRepositoryContentTest } + 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 public void getTestGetProjectWithIllegalArgs() { ItemSelector selector = ArchivaItemSelector.builder( ) @@ -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" ) ); } + } diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java index 8d0a77b08..6c0f7c124 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java @@ -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 ); } |