Adding rest utility component
diff --git a/pom.xml b/pom.xml
index eb54458..190e1f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,7 @@
     <module>spring-cache</module>
     <module>spring-taskqueue</module>
     <module>spring-quartz</module>
+    <module>rest-util</module>
   </modules>
 
   <repositories>
@@ -188,6 +189,12 @@
         <version>${jakarta.annotation.version}</version>
         <scope>provided</scope>
       </dependency>
+      <dependency>
+        <groupId>jakarta.ws.rs</groupId>
+        <artifactId>jakarta.ws.rs-api</artifactId>
+        <version>${jakarta.ws.rs.version}</version>
+        <scope>provided</scope>
+      </dependency>
 
       <dependency>
         <groupId>org.apache.commons</groupId>
@@ -228,6 +235,17 @@
         <scope>test</scope>
       </dependency>
 
+      <dependency>
+        <groupId>io.swagger.core.v3</groupId>
+        <artifactId>swagger-core</artifactId>
+        <version>${io.swagger.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>io.swagger.core.v3</groupId>
+        <artifactId>swagger-annotations</artifactId>
+        <version>${io.swagger.version}</version>
+      </dependency>
+
     </dependencies>
   </dependencyManagement>
   <dependencies>
diff --git a/rest-util/pom.xml b/rest-util/pom.xml
new file mode 100644
index 0000000..ecc9f78
--- /dev/null
+++ b/rest-util/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>archiva-components</artifactId>
+    <groupId>org.apache.archiva.components</groupId>
+    <version>3.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <name>Archiva Components :: REST Utility</name>
+  <description>Utility classes for REST services used by Redback and Archiva.</description>
+
+  <artifactId>archiva-components-rest-util</artifactId>
+
+  <properties>
+    <maven.compiler.source>8</maven.compiler.source>
+    <maven.compiler.target>8</maven.compiler.target>
+    <site.staging.base>${project.parent.basedir}</site.staging.base>
+  </properties>
+
+
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.swagger.core.v3</groupId>
+      <artifactId>swagger-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.swagger.core.v3</groupId>
+      <artifactId>swagger-annotations</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>jakarta.ws.rs</groupId>
+      <artifactId>jakarta.ws.rs-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+  </dependencies>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadocPluginVersion}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <id>aggregate</id>
+            <reports>
+              <report>aggregate</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+    </plugins>
+  </reporting>
+
+
+</project>
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/model/BeanInformation.java b/rest-util/src/main/java/org/apache/archiva/components/rest/model/BeanInformation.java
new file mode 100644
index 0000000..57b3a31
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/model/BeanInformation.java
@@ -0,0 +1,91 @@
+package org.apache.archiva.components.rest.model;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+@XmlRootElement(name="beanInformation")
+public class BeanInformation implements Serializable
+{
+    private static final long serialVersionUID = -432385743277355987L;
+    String id;
+    String displayName;
+    String descriptionKey;
+    String defaultDescription;
+    boolean readonly;
+
+    @Schema(description = "The identifier")
+    public String getId( )
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    @Schema(description = "The display name")
+    public String getDisplayName( )
+    {
+        return displayName;
+    }
+
+    public void setDisplayName( String displayName )
+    {
+        this.displayName = displayName;
+    }
+
+    @Schema(description = "The translation key for the description")
+    public String getDescriptionKey( )
+    {
+        return descriptionKey;
+    }
+
+    public void setDescriptionKey( String descriptionKey )
+    {
+        this.descriptionKey = descriptionKey;
+    }
+
+    @Schema(description = "The description translated in the default language")
+    public String getDefaultDescription( )
+    {
+        return defaultDescription;
+    }
+
+    public void setDefaultDescription( String defaultDescription )
+    {
+        this.defaultDescription = defaultDescription;
+    }
+
+    @Schema(description = "True, if this bean cannot be removed")
+    public boolean isReadonly( )
+    {
+        return readonly;
+    }
+
+    public void setReadonly( boolean readonly )
+    {
+        this.readonly = readonly;
+    }
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/model/PagedResult.java b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PagedResult.java
new file mode 100644
index 0000000..786c1b8
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PagedResult.java
@@ -0,0 +1,71 @@
+package org.apache.archiva.components.rest.model;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
+
+/**
+ * A Paged result puts the data into an envelope
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+@XmlRootElement(name="pagedResult")
+@Schema(name = "PagedResult", description = "Contains paged data. Pages are defined by limit and offset.")
+public class PagedResult<T>
+{
+    PaginationInfo pagination;
+    List<T> data;
+
+    public PagedResult() {
+
+    }
+
+    public PagedResult( int totalCount, int offset, int limit, List<T> data ) {
+        this.data = data;
+        this.pagination = new PaginationInfo( totalCount, offset, limit );
+    }
+
+    public static final <T> PagedResult<T> of(int totalSize, int offset, int limit, List<T> element) {
+        return new PagedResult( totalSize, offset, limit, element);
+    }
+
+    @Schema(description = "This is the payload of the paged data. The type of data depends on the REST method. ")
+    public List<T> getData( )
+    {
+        return data;
+    }
+
+    public void setData( List<T> data )
+    {
+        this.data = data;
+    }
+
+    @Schema(description = "The pagination information")
+    public PaginationInfo getPagination( )
+    {
+        return pagination;
+    }
+
+    public void setPagination( PaginationInfo pagination )
+    {
+        this.pagination = pagination;
+    }
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/model/PaginationInfo.java b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PaginationInfo.java
new file mode 100644
index 0000000..3755522
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PaginationInfo.java
@@ -0,0 +1,82 @@
+package org.apache.archiva.components.rest.model;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ *
+ * Informational attributes for pagination.
+ *
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+@XmlRootElement(name="pagination")
+@Schema(name="PaginationInfo", description = "Contains paging information (limit, offset, totalCount)")
+public class PaginationInfo
+{
+    long totalCount;
+    long offset;
+    long limit;
+
+    public PaginationInfo() {
+
+    }
+
+    public PaginationInfo( long totalCount, long offset, long limit )
+    {
+        this.totalCount = totalCount;
+        this.offset = offset;
+        this.limit = limit;
+    }
+
+    @Schema(description = "The total number of data available.")
+    public long getTotalCount( )
+    {
+        return totalCount;
+    }
+
+    public void setTotalCount( long totalCount )
+    {
+        this.totalCount = totalCount;
+    }
+
+    @Schema(description = "The offset of the first element of the returned dataset.")
+    public long getOffset( )
+    {
+        return offset;
+    }
+
+    public void setOffset( long offset )
+    {
+        this.offset = offset;
+    }
+
+    @Schema(description = "The maximum number of elements returned per page.")
+    public long getLimit( )
+    {
+        return limit;
+    }
+
+    public void setLimit( long limit )
+    {
+        this.limit = limit;
+    }
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/model/PropertyEntry.java b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PropertyEntry.java
new file mode 100644
index 0000000..ef41ef2
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/model/PropertyEntry.java
@@ -0,0 +1,85 @@
+package org.apache.archiva.components.rest.model;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+@XmlRootElement(name="propertyEntry")
+@Schema(name="PropertyEntry", description = "Key/Value-Pair")
+public class PropertyEntry implements Serializable
+{
+    private static final long serialVersionUID = -4486042628710420898L;
+    private String key;
+    private String value;
+
+    public String getKey( )
+    {
+        return key;
+    }
+
+    public void setKey( String key )
+    {
+        this.key = key;
+    }
+
+    public String getValue( )
+    {
+        return value;
+    }
+
+    public void setValue( String value )
+    {
+        this.value = value;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        PropertyEntry that = (PropertyEntry) o;
+
+        if ( !Objects.equals( key, that.key ) ) return false;
+        return Objects.equals( value, that.value );
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        int result = key != null ? key.hashCode( ) : 0;
+        result = 31 * result + ( value != null ? value.hashCode( ) : 0 );
+        return result;
+    }
+
+    @Override
+    public String toString( )
+    {
+        final StringBuilder sb = new StringBuilder( "PropertyEntry{" );
+        sb.append( "key='" ).append( key ).append( '\'' );
+        sb.append( ", value='" ).append( value ).append( '\'' );
+        sb.append( '}' );
+        return sb.toString( );
+    }
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/util/PagingHelper.java b/rest-util/src/main/java/org/apache/archiva/components/rest/util/PagingHelper.java
new file mode 100644
index 0000000..13cbbfe
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/util/PagingHelper.java
@@ -0,0 +1,47 @@
+package org.apache.archiva.components.rest.util;
+
+/*
+ * 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.components.rest.model.PagedResult;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper class for creating paged results.
+ *
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class PagingHelper
+{
+    public static <T> PagedResult<T> getResultFromList( int offset, int limit, List<T> data) {
+        if (offset>=data.size()) {
+            return new PagedResult<>( data.size( ), offset, limit, Collections.emptyList( ) );
+        }
+        int lastIndex = getLastIndex( offset, limit, data.size( ) );
+        return new PagedResult<>( data.size(), offset, limit, data.subList( offset, lastIndex ) );
+    }
+
+    public static int getLastIndex(int offset, int limit, int listSize) {
+        return Math.min( Math.max( 0, offset + limit ), listSize );
+    }
+
+
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/util/QueryHelper.java b/rest-util/src/main/java/org/apache/archiva/components/rest/util/QueryHelper.java
new file mode 100644
index 0000000..a111fab
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/util/QueryHelper.java
@@ -0,0 +1,168 @@
+package org.apache.archiva.components.rest.util;/*
+ * 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.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ *
+ * Helper class that returns combined filter and comparison objects for ordering.
+ *
+ * The query term may be consist of simple query terms separated by whitespace or attribute queries
+ * in the form <code>attribute:query</code>, which means only the attribute is searched for the query string.
+ * <br />
+ * Example:
+ * <dl>
+ *     <dt>`user1 test`</dt>
+ *     <dd>
+ * searches for the tokens user1 and test in the default attributes.
+ * </dd>
+ * <dt>`user1 name:test`</dt>
+ * <dd>searches for the token user1 in the default attributes and for the token test in the attribute name.</dd>
+ * </dl>
+ *
+ *
+ * @since 3.0
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class QueryHelper<T>
+{
+
+    private final Map<String, BiPredicate<String, T>> FILTER_MAP;
+    private final Map<String, Comparator<T>> ORDER_MAP;
+    private final String[] DEFAULT_SEARCH_FIELDS;
+    private final Predicate<T> DEFAULT_FILTER = ( T att ) -> false;
+
+
+    /**
+     * Creates a new query helper with the given filters and comparators.
+     *
+     * @param filterMap a map of filters, where the key is the attribute name and the value is a predicate that matches
+     *                  the filter value and the object instance.
+     * @param orderMap a map of comparators, where key is the attribute name and the value is a comparator for the given
+     *                 object instance
+     * @param defaultSearchFields A array of attribute names, that are used as default search fields.
+     */
+    public QueryHelper(Map<String, BiPredicate<String, T>> filterMap, Map<String, Comparator<T>> orderMap,
+                       String[] defaultSearchFields)
+    {
+        this.FILTER_MAP = filterMap;
+        this.DEFAULT_SEARCH_FIELDS = defaultSearchFields;
+        this.ORDER_MAP = new HashMap<>( orderMap );
+    }
+
+    public <U extends Comparable<? super U>> void addNullsafeFieldComparator( String fieldName, Function<? super T, U> keyExtractor) {
+        ORDER_MAP.put( fieldName, Comparator.comparing( keyExtractor, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) );
+    }
+
+    public void addStringFilter(String attribute, Function<? super T, String> keyExtractor) {
+        this.FILTER_MAP.put( attribute, ( String q, T r ) -> StringUtils.containsIgnoreCase( keyExtractor.apply( r ), q ) );
+    }
+
+    public void addBooleanFilter(String attribute, Function<? super T, Boolean> keyExtractor) {
+        this.FILTER_MAP.put( attribute, ( String q, T r ) -> Boolean.valueOf( q ) == keyExtractor.apply( r ) );
+    }
+
+    /**
+     * Get the comparator for a specific attribute.
+     * @param attributeName the name of the attribute.
+     * @return
+     */
+    Comparator<T> getAttributeComparator( String attributeName )
+    {
+        return ORDER_MAP.get( attributeName );
+    }
+
+    /**
+     * Get the combined order for the given attributes in the given order.
+     *
+     * @param orderBy the attributes to compare. The first attribute in the list will be used first for comparing.
+     * @param ascending
+     * @return
+     */
+    Comparator<T> getComparator( List<String> orderBy, boolean ascending )
+    {
+        if ( ascending )
+        {
+            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) ).filter( Objects::nonNull )
+                .reduce( Comparator::thenComparing )
+                .orElseThrow( () -> new IllegalArgumentException( "No attribute ordering found" ) );
+        }
+
+        else
+        {
+            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) == null ? null : getAttributeComparator( name )
+                .reversed( ) ).filter( Objects::nonNull ).reduce( Comparator::thenComparing )
+                .orElseThrow( () -> new IllegalArgumentException( "No attribute ordering found" ) );
+        }
+    }
+
+    /**
+     * Returns a query filter for a specific attribute and query token.
+     * @param attribute the attribute name to filter for.
+     * @param queryToken the search token.
+     * @return The predicate used to filter the token
+     */
+    Predicate<T> getAttributeQueryFilter( final String attribute, final String queryToken )
+    {
+        if ( FILTER_MAP.containsKey( attribute ) )
+        {
+            return ( T u ) -> FILTER_MAP.get( attribute ).test( queryToken, u );
+        }
+        else
+        {
+            return DEFAULT_FILTER;
+        }
+    }
+
+    /**
+     * Returns the combined query filter for the given query terms.
+     * The query terms may be either simple strings separated by whitespace or use the
+     * <code>attribute:query</code> syntax, that searches only the attribute for the query term.
+     * @param queryTerms the query string
+     * @return the combined query filter
+     */
+    Predicate<T> getQueryFilter( String queryTerms )
+    {
+        return Arrays.stream( queryTerms.split( "\\s+" ) )
+            .map( s -> {
+                    if ( s.contains( ":" ) )
+                    {
+                        String attr = StringUtils.substringBefore( s, ":" );
+                        String term = StringUtils.substringAfter( s, ":" );
+                        return getAttributeQueryFilter( attr, term );
+                    }
+                    else
+                    {
+                        return Arrays.stream( DEFAULT_SEARCH_FIELDS )
+                            .map( att -> getAttributeQueryFilter( att, s ) ).reduce( Predicate::or ).get( );
+                    }
+                }
+            ).reduce( Predicate::or ).get( );
+    }
+
+}
diff --git a/rest-util/src/main/java/org/apache/archiva/components/rest/util/RestUtil.java b/rest-util/src/main/java/org/apache/archiva/components/rest/util/RestUtil.java
new file mode 100644
index 0000000..da745a6
--- /dev/null
+++ b/rest-util/src/main/java/org/apache/archiva/components/rest/util/RestUtil.java
@@ -0,0 +1,59 @@
+package org.apache.archiva.components.rest.util;/*
+ * 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.commons.lang3.StringUtils;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Central utility class that may be used by service implementations.
+ *
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class RestUtil
+{
+    /**
+     * Returns <code>false</code>, if the given parameter is not present in the given uriInfo, or is present and set to 'false' or '0'.
+     * In all other cases it returns <code>true</code>.
+     *
+     * This means you can activate a flag by setting '?param', '?param=true', '?param=1', ...
+     * It is deactivated, if the parameter is absent, or '?param=false', or '?param=0'
+     *
+     * @param uriInfo the uriInfo context instance, that is used to check for the parameter
+     * @param queryParameterName the query parameter name
+     * @return
+     */
+    public static boolean isFlagSet( final UriInfo uriInfo, final String queryParameterName) {
+        MultivaluedMap<String, String> params = uriInfo.getQueryParameters( );
+        if (!params.containsKey( queryParameterName )) {
+            return false;
+        }
+        // parameter is available
+        String value = params.getFirst( queryParameterName );
+        // if its available but without a value it is flagged as present
+        if (StringUtils.isEmpty( value )) {
+            return true;
+        }
+        // if it has a value, we check for false values:
+        if ("false".equalsIgnoreCase( value ) || "0".equalsIgnoreCase( value )) {
+            return false;
+        }
+        return true;
+    }
+}