MINDEXER-91: Add a Spring based example

- Corrected the README.md of the basic module as it was showing the wrong profile.
- Added a minimalistic Spring implementation with a simple service.
- Added two Spring contexts -- one for the actual maven-indexer specific classes and one for the the tests.
- Setup logging via SLF4J + Logback.
- Added README.md.
- Added a simple generator for valid artifact files (useful for testing without needing to have the resources under version control; this could probably be used in the indexer-core as well).
- Added a repository booter which creates test repositories with indexes.
diff --git a/indexer-examples/indexer-examples-basic/README.md b/indexer-examples/indexer-examples-basic/README.md
index de4dc7a..395c662 100644
--- a/indexer-examples/indexer-examples-basic/README.md
+++ b/indexer-examples/indexer-examples-basic/README.md
@@ -7,9 +7,9 @@
 
 ```
 $ cd indexer-examples
-$ mvn clean test -Ptests
+$ mvn clean test -Pexamples
   ... first run will take few minutes to download the index, and then will run showing some output
-$ mvn test -Ptests
+$ mvn test -Pexamples
   ... (no clean goal!) second run will finish quickly, as target folder will already contain an up-to-date index
 ```
 
diff --git a/indexer-examples/indexer-examples-spring/README.md b/indexer-examples/indexer-examples-spring/README.md
new file mode 100644
index 0000000..eb25e72
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/README.md
@@ -0,0 +1,12 @@
+Maven Indexer Examples
+======
+
+This example covers simple use cases and is runnable as Java Junit via the "test" goal.
+
+Execute the following in order to run the example/test:
+
+```
+$ mvn clean test -Pexamples
+```
+
+Please, note that the tests in this module will only compile by default; the will not be executed, unless you activate the profile (-Ptests).
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/IndexerConfiguration.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/IndexerConfiguration.java
new file mode 100644
index 0000000..10229be
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/IndexerConfiguration.java
@@ -0,0 +1,102 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * 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 javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.index.Indexer;
+import org.apache.maven.index.Scanner;
+import org.apache.maven.index.context.IndexCreator;
+
+/**
+ * A simple configuration holder class.
+ * This class contains the mapped indexers.
+ *
+ * @author mtodorov
+ */
+@Named
+@Singleton
+public class IndexerConfiguration
+{
+
+    private Indexer indexer;
+
+    private Scanner scanner;
+
+    private Map<String, IndexCreator> indexers;
+
+
+    @Inject
+    public IndexerConfiguration( Indexer indexer,
+                                 Scanner scanner,
+                                 Map<String, IndexCreator> indexers )
+    {
+        this.indexer = indexer;
+        this.scanner = scanner;
+        this.indexers = indexers;
+    }
+
+    public List<IndexCreator> getIndexersAsList()
+    {
+        List<IndexCreator> indexersAsList = new ArrayList<>();
+        for ( Map.Entry entry : indexers.entrySet() )
+        {
+            indexersAsList.add( ( IndexCreator ) entry.getValue() );
+        }
+
+        return indexersAsList;
+    }
+
+    public Indexer getIndexer()
+    {
+        return indexer;
+    }
+
+    public void setIndexer( Indexer indexer )
+    {
+        this.indexer = indexer;
+    }
+
+    public Scanner getScanner()
+    {
+        return scanner;
+    }
+
+    public void setScanner( Scanner scanner )
+    {
+        this.scanner = scanner;
+    }
+
+    public Map<String, IndexCreator> getIndexers()
+    {
+        return indexers;
+    }
+
+    public void setIndexers( Map<String, IndexCreator> indexers )
+    {
+        this.indexers = indexers;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexManager.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexManager.java
new file mode 100644
index 0000000..536b859
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexManager.java
@@ -0,0 +1,109 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * 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 javax.annotation.PreDestroy;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class represents a mapping between repositoryId-s and their respective indexes.
+ *
+ * @author mtodorov
+ */
+@Singleton
+@Component
+public class RepositoryIndexManager
+{
+
+    private static final Logger logger = LoggerFactory.getLogger( RepositoryIndexManager.class );
+
+    /**
+     * K: repositoryId
+     * V: index
+     */
+    private Map<String, RepositoryIndexer> indexes = new LinkedHashMap<>();
+
+
+    public RepositoryIndexManager()
+    {
+    }
+
+    /**
+     * A convenience method for closing the indexes. This method will be called
+     * by the Spring container and it shouldn't be invoked directly.
+     * <p/>
+     * This method is of particular importance, as you should close the indexes properly.
+     */
+    @PreDestroy
+    private void close()
+    {
+        for ( String repositoryId : indexes.keySet() )
+        {
+            try
+            {
+                final RepositoryIndexer repositoryIndexer = indexes.get( repositoryId );
+
+                logger.debug( "Closing indexer for " + repositoryIndexer.getRepositoryId() + "..." );
+
+                repositoryIndexer.close();
+
+                logger.debug( "Closed indexer for " + repositoryIndexer.getRepositoryId() + "." );
+            }
+            catch ( IOException e )
+            {
+                logger.error( e.getMessage(), e );
+            }
+        }
+    }
+
+    public Map<String, RepositoryIndexer> getIndexes()
+    {
+        return indexes;
+    }
+
+    public void setIndexes( Map<String, RepositoryIndexer> indexes )
+    {
+        this.indexes = indexes;
+    }
+
+    public RepositoryIndexer getRepositoryIndex( String repositoryId )
+    {
+        return indexes.get( repositoryId );
+    }
+
+    public RepositoryIndexer addRepositoryIndex( String repositoryId,
+                                                 RepositoryIndexer value )
+    {
+        return indexes.put( repositoryId, value );
+    }
+
+    public RepositoryIndexer removeRepositoryIndex( String repositoryId )
+    {
+        return indexes.remove( repositoryId );
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexer.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexer.java
new file mode 100644
index 0000000..11a4eb4
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexer.java
@@ -0,0 +1,391 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
+import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.Version;
+import org.apache.maven.index.*;
+import org.apache.maven.index.context.IndexCreator;
+import org.apache.maven.index.context.IndexingContext;
+import org.apache.maven.index.expr.SourcedSearchExpression;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import static java.util.Arrays.asList;
+import static org.apache.lucene.search.BooleanClause.Occur.MUST;
+
+/**
+ * This class provides means to index and search for artifacts in a repository on the file system.
+ *
+ * @author mtodorov
+ */
+public class RepositoryIndexer
+{
+
+    private static final Logger logger = LoggerFactory.getLogger( RepositoryIndexer.class );
+
+    private static final Version luceneVersion = Version.LUCENE_48;
+
+    private static final String[] luceneFields = new String[]{ "g", "a", "v", "p", "c" };
+
+    private static final WhitespaceAnalyzer luceneAnalyzer = new WhitespaceAnalyzer( luceneVersion );
+
+    private Indexer indexer;
+
+    private Scanner scanner;
+
+    private List<IndexCreator> indexers;
+
+    private IndexingContext indexingContext;
+
+    private String repositoryId;
+
+    private File repositoryBasedir;
+
+    private File indexDir;
+
+
+    public RepositoryIndexer()
+    {
+    }
+
+    public void close()
+            throws IOException
+    {
+        indexer.closeIndexingContext( indexingContext, false );
+    }
+
+    public void close( boolean deleteFiles )
+            throws IOException
+    {
+        indexingContext.close( deleteFiles );
+    }
+
+    public void delete( final Collection<ArtifactInfo> artifacts )
+            throws IOException
+    {
+        final List<ArtifactContext> delete = new ArrayList<ArtifactContext>();
+        for ( final ArtifactInfo artifact : artifacts )
+        {
+            logger.debug( "Deleting artifact: {}; ctx id: {}; idx dir: {}",
+                          new String[]{ artifact.toString(),
+                                        indexingContext.getId(),
+                                        indexingContext.getIndexDirectory().toString() } );
+
+            delete.add( new ArtifactContext( null, null, null, artifact, null ) );
+        }
+
+        getIndexer().deleteArtifactsFromIndex( delete, indexingContext );
+    }
+
+    public Set<ArtifactInfo> search( final String groupId,
+                                     final String artifactId,
+                                     final String version,
+                                     final String packaging,
+                                     final String classifier )
+            throws IOException
+    {
+        final BooleanQuery query = new BooleanQuery();
+
+        if ( groupId != null )
+        {
+            query.add( getIndexer().constructQuery( MAVEN.GROUP_ID, new SourcedSearchExpression( groupId ) ), MUST );
+        }
+
+        if ( artifactId != null )
+        {
+            query.add( getIndexer().constructQuery( MAVEN.ARTIFACT_ID, new SourcedSearchExpression( artifactId ) ),
+                       MUST );
+        }
+
+        if ( version != null )
+        {
+            query.add( getIndexer().constructQuery( MAVEN.VERSION, new SourcedSearchExpression( version ) ), MUST );
+        }
+
+        if ( packaging != null )
+        {
+            query.add( getIndexer().constructQuery( MAVEN.PACKAGING, new SourcedSearchExpression( packaging ) ), MUST );
+        }
+        else
+        {
+            // Fallback to jar
+            query.add( getIndexer().constructQuery( MAVEN.PACKAGING, new SourcedSearchExpression( "jar" ) ), MUST );
+        }
+
+        if ( classifier != null )
+        {
+            query.add( getIndexer().constructQuery( MAVEN.CLASSIFIER, new SourcedSearchExpression( classifier ) ),  MUST );
+        }
+
+        logger.debug( "Executing search query: {}; ctx id: {}; idx dir: {}",
+                      new String[]{ query.toString(),
+                                    indexingContext.getId(),
+                                    indexingContext.getIndexDirectory().toString() } );
+
+        final FlatSearchResponse response = getIndexer().searchFlat( new FlatSearchRequest( query, indexingContext ) );
+
+        logger.info( "Hit count: {}", response.getReturnedHitsCount() );
+
+        final Set<ArtifactInfo> results = response.getResults();
+        if ( logger.isDebugEnabled() )
+        {
+            for ( final ArtifactInfo result : results )
+            {
+                logger.debug( "Found artifact: {}", result.toString() );
+            }
+        }
+
+        return results;
+    }
+
+    public Set<ArtifactInfo> search( final String queryText )
+            throws ParseException, IOException
+    {
+        final Query query = new MultiFieldQueryParser( luceneVersion, luceneFields, luceneAnalyzer ).parse( queryText );
+
+        logger.debug( "Executing search query: {}; ctx id: {}; idx dir: {}",
+                      new String[]{ query.toString(),
+                                    indexingContext.getId(),
+                                    indexingContext.getIndexDirectory().toString() } );
+
+        final FlatSearchResponse response = getIndexer().searchFlat( new FlatSearchRequest( query, indexingContext ) );
+
+        final Set<ArtifactInfo> results = response.getResults();
+        if ( logger.isDebugEnabled() )
+        {
+            logger.debug( "Hit count: {}", response.getReturnedHitsCount() );
+
+            for ( final ArtifactInfo result : results )
+            {
+                logger.debug( "Found artifact: {}; uinfo: {}", result.toString(), result.getUinfo() );
+            }
+        }
+
+        return results;
+    }
+
+    public Set<ArtifactInfo> searchBySHA1( final String checksum )
+            throws IOException
+    {
+        final BooleanQuery query = new BooleanQuery();
+        query.add( getIndexer().constructQuery( MAVEN.SHA1, new SourcedSearchExpression( checksum ) ), MUST );
+
+        logger.debug( "Executing search query: {}; ctx id: {}; idx dir: {}",
+                      new String[]{ query.toString(),
+                                    indexingContext.getId(),
+                                    indexingContext.getIndexDirectory().toString() } );
+
+        final FlatSearchResponse response = getIndexer().searchFlat( new FlatSearchRequest( query, indexingContext ) );
+
+        logger.info( "Hit count: {}", response.getReturnedHitsCount() );
+
+        final Set<ArtifactInfo> results = response.getResults();
+        if ( logger.isDebugEnabled() )
+        {
+            for ( final ArtifactInfo result : results )
+            {
+                logger.debug( "Found artifact: {}", result.toString() );
+            }
+        }
+
+        return results;
+    }
+
+    public int index( final File startingPath )
+    {
+        final ScanningResult scan = getScanner().scan( new ScanningRequest( indexingContext,
+                                                                            new ReindexArtifactScanningListener(),
+                                                                            startingPath == null ? "." :
+                                                                            startingPath.getPath() ) );
+        return scan.getTotalFiles();
+    }
+
+    public void addArtifactToIndex( final File artifactFile,
+                                    final ArtifactInfo artifactInfo )
+            throws IOException
+    {
+        getIndexer().addArtifactsToIndex( asList( new ArtifactContext( null, artifactFile, null, artifactInfo, null ) ),
+                                          indexingContext );
+    }
+
+    public void addArtifactToIndex( String repository,
+                                    File artifactFile,
+                                    String groupId,
+                                    String artifactId,
+                                    String version,
+                                    String extension,
+                                    String classifier )
+            throws IOException
+    {
+        ArtifactInfo artifactInfo = new ArtifactInfo( repository,
+                                                      groupId,
+                                                      artifactId,
+                                                      version,
+                                                      classifier,
+                                                      extension );
+        if ( extension != null )
+        {
+            artifactInfo.setFieldValue( MAVEN.EXTENSION, extension );
+        }
+
+        logger.debug( "Adding artifact: {}; repo: {}; type: {}", new String[]{ artifactInfo.getUinfo(),
+                                                                               repository,
+                                                                               extension } );
+
+        getIndexer().addArtifactsToIndex( asList( new ArtifactContext( null,
+                                                                       artifactFile,
+                                                                       null,
+                                                                       artifactInfo,
+                                                                       artifactInfo.calculateGav() ) ),
+                                          indexingContext );
+    }
+
+    private class ReindexArtifactScanningListener
+            implements ArtifactScanningListener
+    {
+
+        int totalFiles = 0;
+        private IndexingContext context;
+
+        @Override
+        public void scanningStarted( final IndexingContext context )
+        {
+            this.context = context;
+        }
+
+        @Override
+        public void scanningFinished( final IndexingContext context,
+                                      final ScanningResult result )
+        {
+            result.setTotalFiles( totalFiles );
+            logger.debug( "Scanning finished; total files: {}; has exception: {}",
+                          result.getTotalFiles(),
+                          result.hasExceptions() );
+        }
+
+        @Override
+        public void artifactError( final ArtifactContext ac,
+                                   final Exception ex )
+        {
+            logger.error( "Artifact error!", ex );
+        }
+
+        @Override
+        public void artifactDiscovered( final ArtifactContext ac )
+        {
+            try
+            {
+                logger.debug( "Adding artifact gav: {}; ctx id: {}; idx dir: {}",
+                              new String[]{ ac.getGav().toString(),
+                                            context.getId(),
+                                            context.getIndexDirectory().toString() } );
+
+                getIndexer().addArtifactsToIndex( asList( ac ), context );
+                totalFiles++;
+            }
+            catch ( IOException ex )
+            {
+                logger.error( "Artifact index error", ex );
+            }
+        }
+    }
+
+    public Indexer getIndexer()
+    {
+        return indexer;
+    }
+
+    public void setIndexer( Indexer indexer )
+    {
+        this.indexer = indexer;
+    }
+
+    public Scanner getScanner()
+    {
+        return scanner;
+    }
+
+    public void setScanner( Scanner scanner )
+    {
+        this.scanner = scanner;
+    }
+
+    public List<IndexCreator> getIndexers()
+    {
+        return indexers;
+    }
+
+    public void setIndexers( List<IndexCreator> indexers )
+    {
+        this.indexers = indexers;
+    }
+
+    public IndexingContext getIndexingContext()
+    {
+        return indexingContext;
+    }
+
+    public void setIndexingContext( IndexingContext indexingContext )
+    {
+        this.indexingContext = indexingContext;
+    }
+
+    public String getRepositoryId()
+    {
+        return repositoryId;
+    }
+
+    public void setRepositoryId( String repositoryId )
+    {
+        this.repositoryId = repositoryId;
+    }
+
+    public File getRepositoryBasedir()
+    {
+        return repositoryBasedir;
+    }
+
+    public void setRepositoryBasedir( File repositoryBasedir )
+    {
+        this.repositoryBasedir = repositoryBasedir;
+    }
+
+    public File getIndexDir()
+    {
+        return indexDir;
+    }
+
+    public void setIndexDir( File indexDir )
+    {
+        this.indexDir = indexDir;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexerFactory.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexerFactory.java
new file mode 100644
index 0000000..b935372
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/RepositoryIndexerFactory.java
@@ -0,0 +1,115 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * 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 javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.maven.index.Indexer;
+import org.apache.maven.index.Scanner;
+import org.apache.maven.index.context.IndexCreator;
+import org.apache.maven.index.context.IndexingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A factory for pre-configured RepositoryIndexers.
+ *
+ * @author mtodorov
+ */
+@Singleton
+public class RepositoryIndexerFactory
+{
+
+    private static final Logger logger = LoggerFactory.getLogger( RepositoryIndexerFactory.class );
+
+    private IndexerConfiguration indexerConfiguration;
+
+
+    @Inject
+    public RepositoryIndexerFactory( IndexerConfiguration indexerConfiguration )
+    {
+        this.indexerConfiguration = indexerConfiguration;
+    }
+
+    public RepositoryIndexer createRepositoryIndexer( String repositoryId,
+                                                      File repositoryBasedir,
+                                                      File indexDir )
+            throws IOException
+    {
+        RepositoryIndexer repositoryIndexer = new RepositoryIndexer();
+        repositoryIndexer.setRepositoryId( repositoryId );
+        repositoryIndexer.setRepositoryBasedir( repositoryBasedir );
+        repositoryIndexer.setIndexDir( indexDir );
+        repositoryIndexer.setIndexingContext( createIndexingContext( repositoryId, repositoryBasedir, indexDir ) );
+        repositoryIndexer.setIndexer( indexerConfiguration.getIndexer() );
+        repositoryIndexer.setScanner( indexerConfiguration.getScanner() );
+
+        return repositoryIndexer;
+    }
+
+    private IndexingContext createIndexingContext( String repositoryId,
+                                                   File repositoryBasedir,
+                                                   File indexDir )
+            throws IOException
+    {
+        return getIndexer().createIndexingContext( repositoryId + "/ctx",
+                                                   repositoryId,
+                                                   repositoryBasedir,
+                                                   indexDir,
+                                                   null,
+                                                   null,
+                                                   true, // if context should be searched in non-targeted mode.
+                                                   true, // if indexDirectory is known to contain (or should contain)
+                                                         // valid Maven Indexer lucene index, and no checks needed to be
+                                                         // performed, or, if we want to "stomp" over existing index
+                                                         // (unsafe to do!).
+                                                   indexerConfiguration.getIndexersAsList() );
+    }
+
+    public IndexerConfiguration getIndexerConfiguration()
+    {
+        return indexerConfiguration;
+    }
+
+    public void setIndexerConfiguration( IndexerConfiguration indexerConfiguration )
+    {
+        this.indexerConfiguration = indexerConfiguration;
+    }
+
+    public Indexer getIndexer()
+    {
+        return indexerConfiguration.getIndexer();
+    }
+
+    public Scanner getScanner()
+    {
+        return indexerConfiguration.getScanner();
+    }
+
+    public Map<String, IndexCreator> getIndexers()
+    {
+        return indexerConfiguration.getIndexers();
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchRequest.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchRequest.java
new file mode 100644
index 0000000..131eb3c
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchRequest.java
@@ -0,0 +1,66 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * 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.
+ */
+
+/**
+ * This class holds the details of the search request.
+ *
+ * @author mtodorov
+ */
+public class SearchRequest
+{
+
+    private String repository;
+
+    private String query;
+
+
+    public SearchRequest()
+    {
+    }
+
+    public SearchRequest( String repository,
+                          String query )
+    {
+        this.repository = repository;
+        this.query = query;
+    }
+
+    public String getRepository()
+    {
+        return repository;
+    }
+
+    public void setRepository( String repository )
+    {
+        this.repository = repository;
+    }
+
+    public String getQuery()
+    {
+        return query;
+    }
+
+    public void setQuery( String query )
+    {
+        this.query = query;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchResults.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchResults.java
new file mode 100644
index 0000000..073e737
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/indexing/SearchResults.java
@@ -0,0 +1,57 @@
+package org.apache.maven.indexer.examples.indexing;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.maven.index.ArtifactInfo;
+
+/**
+ * This is a convenience class for holding search results mapped by repository.
+ *
+ * @author mtodorov
+ */
+public class SearchResults
+{
+
+    /**
+     * K: repositoryId
+     * V: artifactInfos
+     */
+    private Map<String, Collection<ArtifactInfo>> results = new LinkedHashMap<>();
+
+
+    public SearchResults()
+    {
+    }
+
+    public Map<String, Collection<ArtifactInfo>> getResults()
+    {
+        return results;
+    }
+
+    public void setResults( Map<String, Collection<ArtifactInfo>> results )
+    {
+        this.results = results;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/ArtifactIndexingService.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/ArtifactIndexingService.java
new file mode 100644
index 0000000..0a720b4
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/ArtifactIndexingService.java
@@ -0,0 +1,60 @@
+package org.apache.maven.indexer.examples.services;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.maven.indexer.examples.indexing.SearchRequest;
+import org.apache.maven.indexer.examples.indexing.SearchResults;
+
+/**
+ * A simple indexing and search service.
+ *
+ * @author mtodorov
+ */
+public interface ArtifactIndexingService
+{
+
+    void addToIndex( String repositoryId,
+                     File artifactFile,
+                     String groupId,
+                     String artifactId,
+                     String version,
+                     String extension,
+                     String classifier )
+            throws IOException;
+
+    void deleteFromIndex( String repositoryId,
+                          String groupId,
+                          String artifactId,
+                          String version,
+                          String extension,
+                          String classifier )
+            throws IOException;
+
+    SearchResults search( SearchRequest searchRequest )
+            throws IOException, ParseException;
+
+    boolean contains( SearchRequest searchRequest )
+            throws IOException, ParseException;
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/impl/ArtifactIndexingServiceImpl.java b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/impl/ArtifactIndexingServiceImpl.java
new file mode 100644
index 0000000..8f74cf5
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/java/org/apache/maven/indexer/examples/services/impl/ArtifactIndexingServiceImpl.java
@@ -0,0 +1,176 @@
+package org.apache.maven.indexer.examples.services.impl;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.maven.index.ArtifactInfo;
+import org.apache.maven.indexer.examples.indexing.RepositoryIndexManager;
+import org.apache.maven.indexer.examples.indexing.RepositoryIndexer;
+import org.apache.maven.indexer.examples.indexing.SearchRequest;
+import org.apache.maven.indexer.examples.indexing.SearchResults;
+import org.apache.maven.indexer.examples.services.ArtifactIndexingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author mtodorov
+ */
+@Component
+public class ArtifactIndexingServiceImpl
+        implements ArtifactIndexingService
+{
+
+    private static final Logger logger = LoggerFactory.getLogger( ArtifactIndexingServiceImpl.class );
+
+    @Autowired
+    private RepositoryIndexManager repositoryIndexManager;
+
+
+    @Override
+    public void addToIndex( String repositoryId,
+                            File artifactFile,
+                            String groupId,
+                            String artifactId,
+                            String version,
+                            String extension,
+                            String classifier )
+            throws IOException
+    {
+        final RepositoryIndexer indexer = repositoryIndexManager.getRepositoryIndex( repositoryId );
+
+        indexer.addArtifactToIndex( repositoryId, artifactFile, groupId, artifactId, version, extension, classifier );
+    }
+
+    @Override
+    public void deleteFromIndex( String repositoryId,
+                                 String groupId,
+                                 String artifactId,
+                                 String version,
+                                 String extension,
+                                 String classifier )
+            throws IOException
+    {
+        final RepositoryIndexer indexer = repositoryIndexManager.getRepositoryIndex( repositoryId );
+        if ( indexer != null )
+        {
+            indexer.delete( Arrays.asList( new ArtifactInfo( repositoryId,
+                                                             groupId,
+                                                             artifactId,
+                                                             version,
+                                                             classifier,
+                                                             extension ) ) );
+        }
+    }
+
+    @Override
+    public SearchResults search( SearchRequest searchRequest )
+            throws IOException, ParseException
+    {
+        SearchResults searchResults = new SearchResults();
+
+        final String repositoryId = searchRequest.getRepository();
+
+        if ( repositoryId != null && !repositoryId.isEmpty() )
+        {
+            logger.debug( "Repository: {}", repositoryId );
+
+            final Map<String, Collection<ArtifactInfo>> resultsMap = getResultsMap( repositoryId,
+                                                                                    searchRequest.getQuery() );
+
+            if ( !resultsMap.isEmpty() )
+            {
+                searchResults.setResults( resultsMap );
+            }
+
+            if ( logger.isDebugEnabled() )
+            {
+                int results = resultsMap.entrySet().iterator().next().getValue().size();
+
+                logger.debug( "Results: {}", results );
+            }
+        }
+        else
+        {
+            Map<String, Collection<ArtifactInfo>> resultsMap = new LinkedHashMap<>();
+            for ( String repoId : repositoryIndexManager.getIndexes().keySet() )
+            {
+                logger.debug( "Repository: {}", repoId );
+
+                final RepositoryIndexer repositoryIndex = repositoryIndexManager.getRepositoryIndex( repoId );
+                if ( repositoryIndex != null )
+                {
+                    final Set<ArtifactInfo> artifactInfoResults = repositoryIndexManager.getRepositoryIndex( repoId )
+                                                                                        .search( searchRequest.getQuery() );
+
+                    if ( !artifactInfoResults.isEmpty() )
+                    {
+                        resultsMap.put( repoId, artifactInfoResults );
+                    }
+
+                    logger.debug( "Results: {}", artifactInfoResults.size() );
+                }
+            }
+
+            searchResults.setResults( resultsMap );
+        }
+
+        return searchResults;
+    }
+
+    @Override
+    public boolean contains( SearchRequest searchRequest )
+            throws IOException, ParseException
+    {
+        return !getResultsMap( searchRequest.getRepository(), searchRequest.getQuery() ).isEmpty();
+    }
+
+    public Map<String, Collection<ArtifactInfo>> getResultsMap( String repositoryId,
+                                                                String query )
+            throws IOException, ParseException
+    {
+        Map<String, Collection<ArtifactInfo>> resultsMap = new LinkedHashMap<>();
+        final Set<ArtifactInfo> artifactInfoResults = repositoryIndexManager.getRepositoryIndex( repositoryId )
+                                                                            .search( query );
+
+        if ( !artifactInfoResults.isEmpty() )
+        {
+            resultsMap.put( repositoryId, artifactInfoResults );
+        }
+
+        return resultsMap;
+    }
+
+    public RepositoryIndexManager getRepositoryIndexManager()
+    {
+        return repositoryIndexManager;
+    }
+
+    public void setRepositoryIndexManager( RepositoryIndexManager repositoryIndexManager )
+    {
+        this.repositoryIndexManager = repositoryIndexManager;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/main/resources/META-INF/spring/maven-indexer-context.xml b/indexer-examples/indexer-examples-spring/src/main/resources/META-INF/spring/maven-indexer-context.xml
new file mode 100644
index 0000000..2cd5841
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/main/resources/META-INF/spring/maven-indexer-context.xml
@@ -0,0 +1,62 @@
+<?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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+
+    <context:component-scan base-package="org.apache.maven.indexer.examples"/>
+
+    <!-- This context contains the maven-indexer beans and the configuration of the indexing. -->
+
+    <bean id="indexer" class="org.apache.maven.index.DefaultIndexer"/>
+
+    <bean id="scanner" class="org.apache.maven.index.DefaultScanner"/>
+
+    <bean id="indexerEngine" class="org.apache.maven.index.DefaultIndexerEngine"/>
+
+    <bean id="searchEngine" class="org.apache.maven.index.DefaultSearchEngine"/>
+
+    <bean id="queryCreator" class="org.apache.maven.index.DefaultQueryCreator"/>
+
+    <bean id="artifactContextProducer" class="org.apache.maven.index.DefaultArtifactContextProducer"/>
+
+    <bean id="artifactPackagingMapper" class="org.apache.maven.index.artifact.DefaultArtifactPackagingMapper"/>
+
+    <!-- Index creators: -->
+    <bean id="minimalArtifactInfoIndexCreator" class="org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator"/>
+    <bean id="jarFileContentsIndexCreator" class="org.apache.maven.index.creator.JarFileContentsIndexCreator"/>
+    <bean id="mavenPluginArtifactInfoIndexCreator" class="org.apache.maven.index.creator.MavenPluginArtifactInfoIndexCreator"/>
+    <!-- Index creators. -->
+
+    <util:map id="indexers"
+              key-type="java.lang.String"
+              value-type="org.apache.maven.index.creator.AbstractIndexCreator"
+              map-class="java.util.LinkedHashMap">
+        <entry key="min" value-ref="minimalArtifactInfoIndexCreator"/>
+        <entry key="jarContent" value-ref="jarFileContentsIndexCreator"/>
+        <entry key="maven-plugin" value-ref="mavenPluginArtifactInfoIndexCreator"/>
+    </util:map>
+
+</beans>
diff --git a/indexer-examples/indexer-examples-spring/src/site/site.xml b/indexer-examples/indexer-examples-spring/src/site/site.xml
new file mode 100644
index 0000000..5ab71c2
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/site/site.xml
@@ -0,0 +1,42 @@
+<?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/DECORATION/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.1.0 http://maven.apache.org/xsd/decoration-1.1.0.xsd">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Overview">
+      <item name="Introduction" href="index.html"/>
+      <item name="JavaDocs" href="apidocs/index.html"/>
+      <item name="Source Xref" href="xref/index.html"/>
+      <!--item name="FAQ" href="faq.html"/-->
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SimpleArtifactGenerator.java b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SimpleArtifactGenerator.java
new file mode 100644
index 0000000..2ed1c56
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SimpleArtifactGenerator.java
@@ -0,0 +1,175 @@
+package org.apache.maven.indexer.examples;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.DefaultModelWriter;
+import org.apache.maven.model.io.ModelWriter;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * @author mtodorov
+ */
+public class SimpleArtifactGenerator
+{
+
+
+    public SimpleArtifactGenerator()
+    {
+    }
+
+    public File generateArtifact( String repositoryBasedir,
+                                  String groupId,
+                                  String artifactId,
+                                  String version,
+                                  String classifier,
+                                  String extension )
+            throws IOException, NoSuchAlgorithmException, XmlPullParserException
+    {
+        File repositoryDir = new File( repositoryBasedir );
+        File artifactFile = new File( repositoryDir,
+                                      groupId.replaceAll( "\\.", File.separator ) + File.separatorChar +
+                                      artifactId + File.separatorChar + version + File.separatorChar +
+                                      artifactId + "-" + version +
+                                      ( classifier != null ? "-" + classifier + File.separatorChar : "" ) + "." +
+                                      extension );
+
+        if ( !artifactFile.getParentFile().exists() )
+        {
+            //noinspection ResultOfMethodCallIgnored
+            artifactFile.getParentFile().mkdirs();
+        }
+
+        createArchive( artifactFile, groupId, artifactId, version, extension );
+
+        return artifactFile;
+    }
+
+    private void createArchive( File artifactFile,
+                                String groupId,
+                                String artifactId,
+                                String version,
+                                String extension )
+            throws NoSuchAlgorithmException,
+                   IOException, XmlPullParserException
+    {
+        ZipOutputStream zos = null;
+
+        try
+        {
+            // Make sure the artifact's parent directory exists before writing the model.
+            //noinspection ResultOfMethodCallIgnored
+            artifactFile.getParentFile().mkdirs();
+
+            File pomFile = new File( artifactFile.getParent(),
+                                     artifactFile.getName().substring( 0, artifactFile.getName().lastIndexOf( "." ) ) +
+                                     ".pom" );
+
+            zos = new ZipOutputStream( new FileOutputStream( artifactFile ) );
+
+            generatePom( pomFile, groupId, artifactId, version, extension );
+
+            addMavenPomFile( zos, pomFile, groupId, artifactId );
+        }
+        finally
+        {
+            if ( zos != null )
+            {
+                zos.close();
+            }
+        }
+    }
+
+    protected void generatePom( File pomFile,
+                                String groupId,
+                                String artifactId,
+                                String version,
+                                String type )
+            throws IOException,
+                   XmlPullParserException,
+                   NoSuchAlgorithmException
+    {
+
+        // Make sure the artifact's parent directory exists before writing the model.
+        //noinspection ResultOfMethodCallIgnored
+        pomFile.getParentFile().mkdirs();
+
+        Model model = new Model();
+        model.setGroupId( groupId );
+        model.setArtifactId( artifactId );
+        model.setVersion( version );
+        model.setPackaging( type ); // This is not exactly correct.
+
+        ModelWriter writer = new DefaultModelWriter();
+        writer.write( pomFile, null, model );
+
+    }
+
+
+    private void addMavenPomFile( ZipOutputStream zos,
+                                  File pomFile,
+                                  String groupId,
+                                  String artifactId )
+            throws IOException
+    {
+        ZipEntry ze = new ZipEntry( "META-INF/maven/" + groupId + "/" + artifactId + "/" + "pom.xml" );
+        zos.putNextEntry( ze );
+
+        FileInputStream fis = new FileInputStream( pomFile );
+
+        byte[] buffer = new byte[ 1024 ];
+        int len;
+        while ( ( len = fis.read( buffer ) ) > 0 )
+        {
+            zos.write( buffer, 0, len );
+        }
+
+        fis.close();
+        zos.closeEntry();
+    }
+
+    public static String convertGAVToPath( String groupId,
+                                           String artifactId,
+                                           String version,
+                                           String classifier,
+                                           String extension )
+    {
+        String path = "";
+
+        path += groupId.replaceAll( "\\.", "/" ) + "/";
+        path += artifactId + "/";
+        path += version + "/";
+        path += artifactId + "-";
+        path += version;
+        path += classifier != null ? "-" + classifier : "";
+        path += "." + extension;
+
+        return path;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SpringUsageExampleTest.java b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SpringUsageExampleTest.java
new file mode 100644
index 0000000..8c055a7
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/SpringUsageExampleTest.java
@@ -0,0 +1,211 @@
+package org.apache.maven.indexer.examples;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.maven.index.ArtifactInfo;
+import org.apache.maven.indexer.examples.indexing.SearchRequest;
+import org.apache.maven.indexer.examples.indexing.SearchResults;
+import org.apache.maven.indexer.examples.services.ArtifactIndexingService;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import static junit.framework.TestCase.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author mtodorov
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = { "/META-INF/spring/*-context.xml",
+                                    "classpath*:/META-INF/spring/*-context.xml" })
+public class SpringUsageExampleTest
+{
+
+    public static final File REPOSITORIES_BASEDIR = new File( "target/repositories" );
+
+    @Autowired
+    private ArtifactIndexingService artifactIndexingService;
+
+    private SimpleArtifactGenerator generator = new SimpleArtifactGenerator();
+
+
+    @Before
+    public void setUp()
+            throws Exception
+    {
+        if ( !new File( REPOSITORIES_BASEDIR,
+                        "releases/org/apache/maven/indexer/examples/indexer-examples-spring" ).exists() )
+        {
+            //noinspection ResultOfMethodCallIgnored
+            REPOSITORIES_BASEDIR.mkdirs();
+
+            // Generate some valid test artifacts:
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.0",
+                                           "jar",
+                                           null );
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.1",
+                                           "jar",
+                                           null );
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.2",
+                                           "jar",
+                                           null );
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.3",
+                                           "jar",
+                                           null );
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.3.1",
+                                           "jar",
+                                           null );
+            generateArtifactAndAddToIndex( new File( REPOSITORIES_BASEDIR, "releases" ).getAbsolutePath(),
+                                           "releases",
+                                           "org.apache.maven.indexer.examples",
+                                           "indexer-examples-spring",
+                                           "1.4",
+                                           "jar",
+                                           null );
+        }
+    }
+
+    @Test
+    public void testAddAndDelete()
+            throws IOException, ParseException, NoSuchAlgorithmException, XmlPullParserException
+    {
+        // Create a search request matching GAV "org.apache.maven.indexer.examples:indexer-examples-spring:1.4"
+        SearchRequest request = new SearchRequest( "releases",
+                                                   "+g:org.apache.maven.indexer.examples +a:indexer-examples-spring +v:1.4" );
+
+        assertTrue( "Couldn't find existing artifact!", artifactIndexingService.contains( request ) );
+
+        // Delete the artifact from the index:
+        artifactIndexingService.deleteFromIndex( "releases",
+                                                 "org.apache.maven.indexer.examples",
+                                                 "indexer-examples-spring",
+                                                 "1.4",
+                                                 "jar",
+                                                 null );
+
+        assertFalse( "Failed to remove artifact from index!", artifactIndexingService.contains( request ) );
+    }
+
+    @Test
+    public void testSearch()
+            throws IOException, ParseException
+    {
+        // Create a search request matching GAV "org.apache.maven.indexer.examples:indexer-examples-spring:1.3"
+        SearchRequest request = new SearchRequest( "releases",
+                                                   "+g:org.apache.maven.indexer.examples +a:indexer-examples-spring +v:1.3*" );
+
+        assertTrue( "Couldn't find existing artifact!", artifactIndexingService.contains( request ) );
+
+        final SearchResults searchResults = artifactIndexingService.search( request );
+
+        assertFalse( searchResults.getResults().isEmpty() );
+
+        for ( String repositoryId : searchResults.getResults().keySet() )
+        {
+            System.out.println( "Matches in repository " + repositoryId + ":" );
+
+            final Collection<ArtifactInfo> artifactInfos = searchResults.getResults().get( repositoryId );
+            for ( ArtifactInfo artifactInfo : artifactInfos )
+            {
+                System.out.println( "   " +
+                                    artifactInfo.getGroupId() + ":" +
+                                    artifactInfo.getArtifactId() + ":" +
+                                    artifactInfo.getVersion() + ":" +
+                                    artifactInfo.getPackaging() + ":" +
+                                    artifactInfo.getClassifier() );
+            }
+        }
+
+    }
+
+    /**
+     * This method creates some valid dummy artifacts in order to be able to add them to index.
+     *
+     * @param repositoryBasedir
+     * @param repositoryId
+     * @param groupId
+     * @param artifactId
+     * @param version
+     * @param extension
+     * @param classifier
+     * @return
+     * @throws IOException
+     * @throws NoSuchAlgorithmException
+     * @throws XmlPullParserException
+     */
+    private File generateArtifactAndAddToIndex( String repositoryBasedir,
+                                                String repositoryId,
+                                                String groupId,
+                                                String artifactId,
+                                                String version,
+                                                String extension,
+                                                String classifier )
+            throws IOException, NoSuchAlgorithmException, XmlPullParserException
+    {
+        final File artifactFile = generator.generateArtifact( repositoryBasedir,
+                                                              groupId,
+                                                              artifactId,
+                                                              version,
+                                                              classifier,
+                                                              extension );
+
+        // Add the artifact to the index:
+        artifactIndexingService.addToIndex( repositoryId,
+                                            artifactFile,
+                                            groupId,
+                                            artifactId,
+                                            version,
+                                            extension,
+                                            classifier );
+
+        return artifactFile;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/boot/RepositoryBooter.java b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/boot/RepositoryBooter.java
new file mode 100644
index 0000000..19ad7c6
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/test/java/org/apache/maven/indexer/examples/boot/RepositoryBooter.java
@@ -0,0 +1,156 @@
+package org.apache.maven.indexer.examples.boot;
+
+/*
+ * 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 javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.indexer.examples.indexing.RepositoryIndexManager;
+import org.apache.maven.indexer.examples.indexing.RepositoryIndexer;
+import org.apache.maven.indexer.examples.indexing.RepositoryIndexerFactory;
+import org.codehaus.plexus.PlexusContainerException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is a dummy artifact repository creator.
+ *
+ * @author mtodorov
+ */
+@Component
+public class RepositoryBooter
+{
+
+    private static final Logger logger = LoggerFactory.getLogger( RepositoryBooter.class );
+
+    @Autowired
+    private RepositoryIndexManager repositoryIndexManager;
+
+    @Autowired
+    private RepositoryIndexerFactory repositoryIndexerFactory;
+
+
+    public RepositoryBooter()
+    {
+    }
+
+    @PostConstruct
+    public void initialize()
+            throws IOException,
+                   PlexusContainerException,
+                   ComponentLookupException
+    {
+        File repositoriesBaseDir = new File( "target/repositories" );
+
+        if ( !lockExists( repositoriesBaseDir ) )
+        {
+            createLockFile( repositoriesBaseDir );
+            initializeRepositories( repositoriesBaseDir );
+        }
+        else
+        {
+            logger.error( "Failed to initialize the repositories. Another JVM may have already done this." );
+        }
+
+        logger.debug( "Initialized repositories." );
+    }
+
+    private void createLockFile( File repositoriesRootDir )
+            throws IOException
+    {
+        final File lockFile = new File( repositoriesRootDir, "repositories.lock" );
+        //noinspection ResultOfMethodCallIgnored
+        lockFile.getParentFile().mkdirs();
+        //noinspection ResultOfMethodCallIgnored
+        lockFile.createNewFile();
+    }
+
+    private boolean lockExists( File repositoriesRootDir )
+            throws IOException
+    {
+        File lockFile = new File( repositoriesRootDir, "repositories.lock" );
+
+        return lockFile.exists();
+    }
+
+    private void initializeRepositories( File repositoriesBaseDir )
+            throws IOException,
+                   PlexusContainerException,
+                   ComponentLookupException
+    {
+        initializeRepository( repositoriesBaseDir, "releases" );
+        initializeRepository( repositoriesBaseDir, "snapshots" );
+    }
+
+    private void initializeRepository( File repositoriesBaseDir,
+                                       String repositoryName )
+            throws IOException,
+                   PlexusContainerException,
+                   ComponentLookupException
+    {
+        createRepositoryStructure( repositoriesBaseDir.getAbsolutePath(), repositoryName );
+
+        initializeRepositoryIndex( new File( repositoriesBaseDir.getAbsoluteFile(), repositoryName ), repositoryName );
+    }
+
+    public void createRepositoryStructure( String repositoriesBaseDir,
+                                           String repositoryName )
+            throws IOException
+    {
+        final File repositoriesBasedir = new File( repositoriesBaseDir );
+        //noinspection ResultOfMethodCallIgnored
+        new File( repositoriesBasedir, repositoryName ).mkdirs();
+        //noinspection ResultOfMethodCallIgnored
+        new File( repositoriesBasedir, repositoryName + File.separatorChar + ".index" ).mkdirs();
+
+        logger.debug( "Created directory structure for repository '" +
+                      repositoriesBasedir.getAbsolutePath() + File.separatorChar + repositoryName + "'." );
+    }
+
+    private void initializeRepositoryIndex( File repositoryBasedir,
+                                            String repositoryId )
+            throws PlexusContainerException,
+                   ComponentLookupException,
+                   IOException
+    {
+        final File indexDir = new File( repositoryBasedir, ".index" );
+
+        RepositoryIndexer repositoryIndexer = repositoryIndexerFactory.createRepositoryIndexer( repositoryId,
+                                                                                                repositoryBasedir,
+                                                                                                indexDir );
+
+        repositoryIndexManager.addRepositoryIndex( repositoryId, repositoryIndexer );
+    }
+
+    public RepositoryIndexManager getRepositoryIndexManager()
+    {
+        return repositoryIndexManager;
+    }
+
+    public void setRepositoryIndexManager( RepositoryIndexManager repositoryIndexManager )
+    {
+        this.repositoryIndexManager = repositoryIndexManager;
+    }
+
+}
diff --git a/indexer-examples/indexer-examples-spring/src/test/resources/META-INF/spring/indexer-examples-context.xml b/indexer-examples/indexer-examples-spring/src/test/resources/META-INF/spring/indexer-examples-context.xml
new file mode 100644
index 0000000..e0a2cf7
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/test/resources/META-INF/spring/indexer-examples-context.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+
+    <context:component-scan base-package="org.apache.maven.indexer.examples"/>
+
+    <bean id="indexerConfiguration" class="org.apache.maven.indexer.examples.indexing.IndexerConfiguration">
+        <constructor-arg name="indexer" ref="indexer"/>
+        <constructor-arg name="indexers" ref="indexers"/>
+        <constructor-arg name="scanner" ref="scanner"/>
+    </bean>
+
+    <bean id="repositoryIndexerFactory" class="org.apache.maven.indexer.examples.indexing.RepositoryIndexerFactory"/>
+
+    <bean id="repositoryIndexManager" class="org.apache.maven.indexer.examples.indexing.RepositoryIndexManager"/>
+
+    <bean id="artifactIndexingService" class="org.apache.maven.indexer.examples.services.impl.ArtifactIndexingServiceImpl"/>
+
+    <bean id="repositoryBooter" class="org.apache.maven.indexer.examples.boot.RepositoryBooter"/>
+
+</beans>
diff --git a/indexer-examples/indexer-examples-spring/src/test/resources/logback.xml b/indexer-examples/indexer-examples-spring/src/test/resources/logback.xml
new file mode 100644
index 0000000..a27f7b5
--- /dev/null
+++ b/indexer-examples/indexer-examples-spring/src/test/resources/logback.xml
@@ -0,0 +1,35 @@
+<!--
+    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.
+-->
+
+<configuration>
+
+  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>[%p] %c - %m%n</pattern>
+    </encoder>
+  </appender>
+
+  <logger name="org.springframework" level="ERROR" />
+  <logger name="org.apache.maven.indexer.examples.indexing" level="DEBUG" />
+  <logger name="org.apache.maven.indexer.examples.boot" level="ERROR" />
+
+  <root level="ERROR">
+    <appender-ref ref="stdout" />
+  </root>
+
+</configuration>
\ No newline at end of file