Adding new item type for additional files that are not real artifacts

This commit is contained in:
Martin Stockhammer 2020-05-03 11:34:31 +02:00
parent da923ece67
commit 56de9e590b
13 changed files with 634 additions and 35 deletions

View File

@ -0,0 +1,101 @@
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.Artifact;
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.Version;
/**
*
* Deletion status of a given item.
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public class ItemDeleteStatus
{
public static final int OK = 0;
public static final int DELETION_FAILED = 1;
public static final int ITEM_NOT_FOUND = 2;
public static final int UNKNOWN = 128;
private ContentItem item;
private int status;
private Throwable exception;
public ItemDeleteStatus(ContentItem item) {
this.item = item;
this.status = OK;
}
public ItemDeleteStatus(ContentItem item, int status) {
this.item = item;
this.status = status;
}
public ItemDeleteStatus(ContentItem item, int status, Throwable e) {
this.item = item;
this.status = status;
this.exception = e;
}
public ContentItem getItem( )
{
return item;
}
public int getStatus( )
{
return status;
}
public Throwable getException( )
{
return exception;
}
public Class<? extends ContentItem> getItemType() {
if (item instanceof Namespace ) {
return Namespace.class;
} else if (item instanceof Project ) {
return Project.class;
} else if (item instanceof Version ) {
return Version.class;
} else if (item instanceof Artifact ) {
return Artifact.class;
} else {
return ContentItem.class;
}
}
public <U extends ContentItem> U adapt(Class<U> clazz) throws IllegalArgumentException {
if (clazz.isAssignableFrom( item.getClass() )) {
return (U) item;
} else {
throw new IllegalArgumentException( "Cannot convert instance of " + item.getClass( ) + " to " + clazz );
}
}
}

View File

@ -33,8 +33,10 @@ import org.apache.archiva.repository.content.Version;
import org.apache.archiva.repository.storage.StorageAsset;
import java.nio.file.Path;
import java.util.BitSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
@ -48,6 +50,27 @@ public interface ManagedRepositoryContent extends RepositoryContent
/// ***************** 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.
@ -137,11 +160,11 @@ public interface ManagedRepositoryContent extends RepositoryContent
* <li>artifactId or projectId</li>
* </ul>
* If the coordinates do not provide enough information for selecting a artifact, a {@link IllegalArgumentException} will be thrown
* It depends on the repository type, what exactly is deleted for a given set of coordinates. Some repository type
* It depends on the repository type, what exactly is returned for a given set of coordinates. Some repository type
* may have different required and optional coordinates. For further information please check the documentation for the
* type specific implementations.
*
* The following coordinates are optional and may further specify the artifact to delete.
* The following coordinates are optional and may further specify the artifact to return.
* <ul>
* <li>classifier</li>
* <li>type</li>
@ -188,6 +211,19 @@ 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.

View File

@ -34,7 +34,7 @@ import org.apache.archiva.model.ArtifactReference;
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public interface Artifact extends ContentItem
public interface Artifact extends DataItem
{
/**
@ -45,6 +45,7 @@ public interface Artifact extends ContentItem
*
* @return the identifier of the artifact. Never returns <code>null</code> or empty string
*/
@Override
String getId( );
/**
@ -82,11 +83,34 @@ public interface Artifact extends ContentItem
*/
String getClassifier( );
/**
* This may be different from extension and gives the remainder that is used to build the file path from
* the artifact coordinates (namespace, id, version, classifier, type)
*
* @return the file name remainder
*/
String getRemainder( );
/**
* Returns the type of the artifact
* @return
*/
@Override
ArtifactType getDataType();
/**
* Returns a unique key
* @return
*/
String toKey();
/**
* Short cut for the file name. Should always return the same value as the artifact name.
*
* @return the name of the file
*/
@Override
default String getFileName( )
{
return getAsset( ).getName( );
@ -98,6 +122,7 @@ public interface Artifact extends ContentItem
*
* @return the file name extension
*/
@Override
default String getExtension( )
{
final String name = getAsset( ).getName( );
@ -112,33 +137,8 @@ public interface Artifact extends ContentItem
}
}
/**
* This may be different from extension and gives the remainder that is used to build the file path from
* the artifact coordinates (namespace, id, version, classifier, type)
*
* @return the file name remainder
*/
String getRemainder( );
/**
* Should return the mime type of the artifact.
*
* @return the mime type of the artifact.
*/
String getContentType( );
/**
* Returns the type of the artifact
* @return
*/
ArtifactType getArtifactType();
/**
* Returns a unique key
* @return
*/
String toKey();
default ContentItem getParent( )
{
return getVersion();
}
}

View File

@ -24,7 +24,7 @@ package org.apache.archiva.repository.content;
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public interface ArtifactType
public interface ArtifactType extends DataItemType
{
String name();
}

View File

@ -0,0 +1,27 @@
package org.apache.archiva.repository.content;
/*
* 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 enum BaseDataItemTypes implements DataItemType
{
METADATA,UNKNOWN
}

View File

@ -0,0 +1,87 @@
package org.apache.archiva.repository.content;
/*
* 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.
*/
/**
*
* A data item is a item that is not a real artifact because it does not have
* a version, but is normally file based.
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
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
*/
String getId( );
/**
* Returns the extension of the file. This method should always return the extension string after the last
* '.'-character.
*
* @return the file name extension
*/
default String getExtension( )
{
final String name = getAsset( ).getName( );
final int idx = name.lastIndexOf( '.' )+1;
if ( idx > 0 )
{
return name.substring( idx );
}
else
{
return "";
}
}
/**
* Should return the mime type of the artifact.
*
* @return the mime type of the artifact.
*/
String getContentType( );
/**
* Short cut for the file name. Should always return the same value as the artifact name.
*
* @return the name of the file
*/
default String getFileName( )
{
return getAsset( ).getName( );
}
/**
* Returns the
* @return the type of the item
*/
DataItemType getDataType();
}

View File

@ -0,0 +1,28 @@
package org.apache.archiva.repository.content;
/*
* 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.
*/
/**
* The data item type
* @author Martin Stockhammer <martin_s@apache.org>
*/
public interface DataItemType
{
String name();
}

View File

@ -22,6 +22,8 @@ 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;
@ -66,7 +68,6 @@ public class ArchivaArtifact extends ArchivaContentItem implements Artifact
}
@Override
public String getId( )
{
@ -110,7 +111,7 @@ public class ArchivaArtifact extends ArchivaContentItem implements Artifact
}
@Override
public ArtifactType getArtifactType( )
public ArtifactType getDataType( )
{
return artifactType;
}

View File

@ -0,0 +1,223 @@
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.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;
import org.apache.archiva.repository.content.base.builder.DataItemWithIdBuilder;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.commons.lang3.StringUtils;
/**
* Base implementation of artifact.
* <p>
* You have to use the builder method {@link #withAsset(StorageAsset)} to create a instance.
* The build() method can be called after the required attributes are set.
* <p>
* Artifacts are equal if the following coordinates match:
* <ul>
* <li>repository</li>
* <li>asset</li>
* <li>version</li>
* <li>artifactId</li>
* <li>artifactVersion</li>
* <li>type</li>
* <li>classifier</li>
* <li>artifactType</li>
* </ul>
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public class ArchivaDataItem extends ArchivaContentItem implements DataItem
{
private String id;
private ContentItem parent;
private String contentType;
private DataItemType dataItemType;
private ArchivaDataItem( )
{
}
@Override
public String getId( )
{
return id;
}
@Override
public ContentItem getParent( )
{
return parent;
}
@Override
public String getContentType( )
{
return contentType;
}
@Override
public DataItemType getDataType( )
{
return dataItemType;
}
/**
* Returns the builder for creating a new artifact instance. You have to fill the
* required attributes before the build() method is available.
*
* @param asset the storage asset representing the artifact
* @return a builder for creating new artifact instance
*/
public static DataItemWithIdBuilder withAsset( StorageAsset asset )
{
return new Builder( ).withAsset( asset );
}
@Override
public boolean equals( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass( ) != o.getClass( ) ) return false;
if ( !super.equals( o ) ) return false;
ArchivaDataItem that = (ArchivaDataItem) o;
if ( !id.equals( that.id ) ) return false;
if ( !parent.equals( that.parent ) ) return false;
return dataItemType.equals(that.dataItemType );
}
@Override
public int hashCode( )
{
int result = super.hashCode( );
result = 31 * result + id.hashCode( );
result = 31 * result + parent.hashCode( );
result = 31 * result + dataItemType.hashCode( );
return result;
}
@Override
public String toString( )
{
final StringBuilder sb = new StringBuilder( "ArchivaArtifact{" );
sb.append( "id='" ).append( id ).append( '\'' );
sb.append( ", parent=" ).append( parent );
sb.append( ", contentType='" ).append( contentType ).append( '\'' );
sb.append( ", artifactType=" ).append( dataItemType );
sb.append( '}' );
return sb.toString( );
}
public static String defaultString( String value )
{
if ( value == null )
{
return "";
}
return value.trim();
}
private static class Builder
extends ContentItemBuilder<ArchivaDataItem, DataItemOptBuilder, DataItemWithIdBuilder >
implements DataItemOptBuilder,DataItemWithIdBuilder
{
Builder( )
{
super( new ArchivaDataItem( ) );
}
@Override
protected DataItemOptBuilder getOptBuilder( )
{
return this;
}
@Override
protected DataItemWithIdBuilder getNextBuilder( )
{
return this;
}
@Override
public DataItemOptBuilder withParent( ContentItem parent )
{
if ( parent == null )
{
throw new IllegalArgumentException( "version may not be null" );
}
item.parent = parent;
super.setRepository( parent.getRepository( ) );
return this;
}
@Override
public DataItemOptBuilder withId( String id )
{
if ( StringUtils.isEmpty( id ) )
{
throw new IllegalArgumentException( "Artifact id may not be null or empty" );
}
item.id = id;
return this;
}
@Override
public DataItemOptBuilder withContentType( String contentType )
{
item.contentType = contentType;
return this;
}
@Override
public DataItemOptBuilder withDataType( DataItemType type )
{
item.dataItemType = type;
return this;
}
@Override
public ArchivaDataItem build( )
{
super.build( );
if ( item.contentType == null )
{
item.contentType = "";
}
if (item.dataItemType ==null) {
item.dataItemType = BaseDataItemTypes.UNKNOWN;
}
return item;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
package org.apache.archiva.repository.content.base.builder;
import org.apache.archiva.repository.content.ArtifactType;
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.ArchivaArtifact;
import org.apache.archiva.repository.content.base.ArchivaDataItem;
/**
* @author Martin Stockhammer <martin_s@apache.org>
*/
public interface DataItemOptBuilder
extends OptBuilder<ArchivaDataItem, DataItemOptBuilder>
{
DataItemOptBuilder withParent( ContentItem parent );
DataItemOptBuilder withContentType( String contentType );
DataItemOptBuilder withDataType( DataItemType type );
ArchivaDataItem build( );
}

View File

@ -0,0 +1,27 @@
/*
* 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.
*/
package org.apache.archiva.repository.content.base.builder;
/**
* @author Martin Stockhammer <martin_s@apache.org>
*/
public interface DataItemWithIdBuilder
{
DataItemOptBuilder withId( String id );
}

View File

@ -25,6 +25,7 @@ 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.ItemDeleteStatus;
import org.apache.archiva.repository.LayoutException;
import org.apache.archiva.repository.ManagedRepository;
import org.apache.archiva.repository.ManagedRepositoryContent;
@ -40,6 +41,7 @@ import org.springframework.stereotype.Service;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
@ -62,6 +64,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
return null;
}
@Override
public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException
{
}
@Override
public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
{
@ -125,6 +133,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
return null;
}
@Override
public Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException
{
return null;
}
@Override
public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
{

View File

@ -45,6 +45,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;
@ -78,6 +79,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
return null;
}
@Override
public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException
{
}
@Override
public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
{
@ -126,6 +133,12 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
return null;
}
@Override
public Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException
{
return null;
}
@Override
public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
{