]> source.dussan.org Git - archiva.git/commitdiff
* Adding CrossRepositorySearch component.
authorJoakim Erdfelt <joakime@apache.org>
Tue, 24 Apr 2007 05:40:19 +0000 (05:40 +0000)
committerJoakim Erdfelt <joakime@apache.org>
Tue, 24 Apr 2007 05:40:19 +0000 (05:40 +0000)
git-svn-id: https://svn.apache.org/repos/asf/maven/archiva/branches/archiva-jpox-database-refactor@531746 13f79535-47bb-0310-9956-ffa450edef68

22 files changed:
archiva-base/archiva-indexer/pom.xml
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/RepositoryContentIndex.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeHandlers.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeKeys.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentHandlers.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentKeys.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/hashcodes/HashcodesHandlers.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/hashcodes/HashcodesKeys.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/lucene/LuceneIndexHandlers.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/lucene/LuceneRepositoryContentIndex.java
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/AbstractIndexerTestCase.java
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/MockConfiguration.java
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/BytecodeIndexPopulator.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/FileContentIndexPopulator.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/HashcodesIndexPopulator.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/IndexPopulator.java [new file with mode: 0644]
archiva-base/archiva-indexer/src/test/resources/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.xml [new file with mode: 0644]
archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java

index 7d77d445fc871c0917f5739e63a443433e3cc2bf..dab7595b2ccc35ceca0d083b2e16441f3e6e3218 100644 (file)
@@ -31,7 +31,7 @@
   <dependencies>
     <dependency>
       <groupId>org.apache.maven.archiva</groupId>
-      <artifactId>archiva-configuration</artifactId>
+      <artifactId>archiva-repository-layer</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.archiva</groupId>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
     </dependency>
+    <dependency>
+      <groupId>easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <version>1.2_Java1.3</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
index 414bdc51de21eb81f51943d677a65ca08cfcb0ee..18ec21887396450ef0363352b078b485dce5a68a 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.maven.archiva.indexer;
  * under the License.
  */
 
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.maven.archiva.indexer.lucene.LuceneRepositoryContentRecord;
 import org.apache.maven.archiva.indexer.query.Query;
 
@@ -41,23 +42,23 @@ public interface RepositoryContentIndex
      */
     void indexRecords( Collection records )
         throws RepositoryIndexException;
-    
+
     /**
      * Modify (potentially) existing records in the index.
      * 
      * @param records the collection of {@link LuceneRepositoryContentRecord} objects to modify in the index.
      * @throws RepositoryIndexException if there is a problem modifying the records.
      */
-    public void modifyRecords( Collection records ) 
+    public void modifyRecords( Collection records )
         throws RepositoryIndexException;
-    
+
     /**
      * Modify an existing (potential) record in the index.
      *  
      * @param record the record to modify.
      * @throws RepositoryIndexException if there is a problem modifying the record.
      */
-    public void modifyRecord( LuceneRepositoryContentRecord record ) 
+    public void modifyRecord( LuceneRepositoryContentRecord record )
         throws RepositoryIndexException;
 
     /**
@@ -106,11 +107,25 @@ public interface RepositoryContentIndex
      */
     Collection getAllRecordKeys()
         throws RepositoryIndexException;
-    
+
     /**
      * Get the index directory.
      * 
      * @return the index directory.
      */
     File getIndexDirectory();
+
+    /**
+     * Get the {@link QueryParser} appropriate for searches within this index.
+     * 
+     * @return the query parser;
+     */
+    QueryParser getQueryParser();
+
+    /**
+     * Get the id of index.
+     * 
+     * @return the id of index.
+     */
+    String getId();
 }
index b78f9bf7f6f9327657b04f5682c7f897a060940b..4c8056c58c170867f529830e38e4a7b8e042c395 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.maven.archiva.indexer.bytecode;
  */
 
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.maven.archiva.indexer.lucene.LuceneEntryConverter;
 import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
 
@@ -29,16 +31,33 @@ import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
-public class BytecodeHandlers implements LuceneIndexHandlers
+public class BytecodeHandlers
+    implements LuceneIndexHandlers
 {
     private BytecodeAnalyzer analyzer;
 
     private BytecodeEntryConverter converter;
 
+    private QueryParser queryParser;
+
     public BytecodeHandlers()
     {
         converter = new BytecodeEntryConverter();
         analyzer = new BytecodeAnalyzer();
+        queryParser = new MultiFieldQueryParser( new String[] {
+            BytecodeKeys.GROUPID,
+            BytecodeKeys.ARTIFACTID,
+            BytecodeKeys.VERSION,
+            BytecodeKeys.CLASSIFIER,
+            BytecodeKeys.TYPE,
+            BytecodeKeys.CLASSES,
+            BytecodeKeys.FILES,
+            BytecodeKeys.METHODS }, analyzer );
+    }
+    
+    public String getId()
+    {
+        return BytecodeKeys.ID;
     }
 
     public Analyzer getAnalyzer()
@@ -50,4 +69,9 @@ public class BytecodeHandlers implements LuceneIndexHandlers
     {
         return converter;
     }
+
+    public QueryParser getQueryParser()
+    {
+        return queryParser;
+    }
 }
index 5150d24f34136606da1760e97a5fe15facd0f4be..9d624306605aae81724f54b322ffadf5dd6ba3fa 100644 (file)
@@ -27,13 +27,16 @@ import org.apache.maven.archiva.indexer.ArtifactKeys;
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
-public class BytecodeKeys extends ArtifactKeys
+public class BytecodeKeys
+    extends ArtifactKeys
 {
+    public static final String ID = "bytecode";
+
     public static final String CLASSES = "classes";
 
     public static final String METHODS = "methods";
 
     public static final String FILES = "files";
-    
+
     public static final String JDK = "jdk";
 }
index 1a3e5b40ffb55c82a78cbbd0c87cc9c0d0bee4a6..229a8e621392050a718f101275cdf939ee25b5e3 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.maven.archiva.indexer.filecontent;
  */
 
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.maven.archiva.indexer.lucene.LuceneEntryConverter;
 import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
 
@@ -29,10 +31,28 @@ import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
-public class FileContentHandlers implements LuceneIndexHandlers
+public class FileContentHandlers
+    implements LuceneIndexHandlers
 {
     private FileContentAnalyzer analyzer;
 
+    private FileContentConverter converter;
+
+    private QueryParser queryParser;
+
+    public FileContentHandlers()
+    {
+        analyzer = new FileContentAnalyzer();
+        converter = new FileContentConverter();
+        queryParser = new MultiFieldQueryParser( new String[] { FileContentKeys.FILENAME, FileContentKeys.CONTENT },
+                                                 analyzer );
+    }
+
+    public String getId()
+    {
+        return FileContentKeys.ID;
+    }
+
     public Analyzer getAnalyzer()
     {
         return analyzer;
@@ -40,6 +60,11 @@ public class FileContentHandlers implements LuceneIndexHandlers
 
     public LuceneEntryConverter getConverter()
     {
-        return null;
+        return converter;
+    }
+
+    public QueryParser getQueryParser()
+    {
+        return queryParser;
     }
 }
index e354a4464d651812b6dbe0fec75f507377d2c5a5..7fcab6eb1981a8ff9e9891650b829fb0c2d9bf15 100644 (file)
@@ -27,6 +27,8 @@ package org.apache.maven.archiva.indexer.filecontent;
  */
 public class FileContentKeys
 {
+    public static final String ID = "filecontent";
+
     public static final String FILENAME = "filename";
 
     public static final String CONTENT = "content";
index fb23f04890ccf72c7a9f44fe69836fd17cf1671e..645ee66d82a12c282506ba35c50e7b4614b5e3c3 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.maven.archiva.indexer.hashcodes;
  */
 
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.maven.archiva.indexer.lucene.LuceneEntryConverter;
 import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
 
@@ -29,16 +31,32 @@ import org.apache.maven.archiva.indexer.lucene.LuceneIndexHandlers;
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
-public class HashcodesHandlers implements LuceneIndexHandlers
+public class HashcodesHandlers
+    implements LuceneIndexHandlers
 {
     private HashcodesAnalyzer analyzer;
 
     private HashcodesEntryConverter converter;
 
+    private QueryParser queryParser;
+
     public HashcodesHandlers()
     {
         converter = new HashcodesEntryConverter();
         analyzer = new HashcodesAnalyzer();
+        queryParser = new MultiFieldQueryParser( new String[] {
+            HashcodesKeys.GROUPID,
+            HashcodesKeys.ARTIFACTID,
+            HashcodesKeys.VERSION,
+            HashcodesKeys.CLASSIFIER,
+            HashcodesKeys.TYPE,
+            HashcodesKeys.MD5,
+            HashcodesKeys.SHA1 }, analyzer );
+    }
+
+    public String getId()
+    {
+        return HashcodesKeys.ID;
     }
 
     public Analyzer getAnalyzer()
@@ -50,4 +68,9 @@ public class HashcodesHandlers implements LuceneIndexHandlers
     {
         return converter;
     }
+
+    public QueryParser getQueryParser()
+    {
+        return queryParser;
+    }
 }
index 72f620aad03a96481817166aa04c4da50915d6ed..8b7d540e7e4c7c752486f58fda537962dd12ca88 100644 (file)
@@ -29,6 +29,8 @@ import org.apache.maven.archiva.indexer.ArtifactKeys;
  */
 public class HashcodesKeys extends ArtifactKeys
 {
+    public static final String ID = "hashcodes";
+    
     public static final String MD5 = "md5";
 
     public static final String SHA1 = "sha1";
index 4dd25c31602ddcce78689d92f3781f763195e5d9..762db18d0592162f8aa3482c99906d16d07314f1 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.maven.archiva.indexer.lucene;
  */
 
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.queryParser.QueryParser;
 
 /**
  * The important bits and pieces for handling a specific lucene index    
@@ -42,4 +43,18 @@ public interface LuceneIndexHandlers
      * @return the analzer to use.
      */
     public Analyzer getAnalyzer();
+    
+    /**
+     * Get the {@link QueryParser} appropriate for searches within this index.
+     * 
+     * @return the query parser.
+     */
+    public QueryParser getQueryParser();
+
+    /**
+     * Get the id of the index handler.
+     * 
+     * @return the id of the index handler.
+     */
+    public String getId();
 }
index 143525ccf00f297f1935e4cade31bd35a05d51a6..0e1241d611154319c95ee2b9f7d0c52e029c123f 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.lucene.search.Hits;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MatchAllDocsQuery;
@@ -46,18 +47,19 @@ import java.util.List;
  *
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
  */
-public class LuceneRepositoryContentIndex implements RepositoryContentIndex
+public class LuceneRepositoryContentIndex
+    implements RepositoryContentIndex
 {
     /**
      * The max field length for a field in a document.
      */
     private static final int MAX_FIELD_LENGTH = 40000;
-    
+
     /**
      * The location of the index on the file system.
      */
     private File indexLocation;
-    
+
     /**
      * The Lucene Index Handlers
      */
@@ -69,14 +71,16 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         this.indexHandlers = handlers;
     }
 
-    public void indexRecords( Collection records ) throws RepositoryIndexException
+    public void indexRecords( Collection records )
+        throws RepositoryIndexException
     {
         deleteRecords( records );
 
         addRecords( records );
     }
-    
-    public void modifyRecords( Collection records ) throws RepositoryIndexException
+
+    public void modifyRecords( Collection records )
+        throws RepositoryIndexException
     {
         IndexModifier indexModifier = null;
         try
@@ -111,7 +115,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    public void modifyRecord( LuceneRepositoryContentRecord record ) throws RepositoryIndexException
+    public void modifyRecord( LuceneRepositoryContentRecord record )
+        throws RepositoryIndexException
     {
         IndexModifier indexModifier = null;
         try
@@ -141,7 +146,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    private void addRecords( Collection records ) throws RepositoryIndexException
+    private void addRecords( Collection records )
+        throws RepositoryIndexException
     {
         IndexWriter indexWriter;
         try
@@ -180,7 +186,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    public void deleteRecords( Collection records ) throws RepositoryIndexException
+    public void deleteRecords( Collection records )
+        throws RepositoryIndexException
     {
         if ( exists() )
         {
@@ -212,17 +219,20 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    public Collection getAllRecords() throws RepositoryIndexSearchException
+    public Collection getAllRecords()
+        throws RepositoryIndexSearchException
     {
         return search( new LuceneQuery( new MatchAllDocsQuery() ) );
     }
 
-    public Collection getAllRecordKeys() throws RepositoryIndexException
+    public Collection getAllRecordKeys()
+        throws RepositoryIndexException
     {
         return getAllFieldValues( LuceneDocumentMaker.PRIMARY_KEY );
     }
 
-    private List getAllFieldValues( String fieldName ) throws RepositoryIndexException
+    private List getAllFieldValues( String fieldName )
+        throws RepositoryIndexException
     {
         List keys = new ArrayList();
 
@@ -258,65 +268,66 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         return keys;
     }
 
-//    public List getAllGroupIds() throws RepositoryIndexException
-//    {
-//        return getAllFieldValues( StandardIndexRecordFields.GROUPID_EXACT );
-//    }
-//
-//    public List getArtifactIds( String groupId ) throws RepositoryIndexSearchException
-//    {
-//        return searchField( new TermQuery( new Term( StandardIndexRecordFields.GROUPID_EXACT, groupId ) ),
-//                            StandardIndexRecordFields.ARTIFACTID );
-//    }
-//
-//    public List getVersions( String groupId, String artifactId ) throws RepositoryIndexSearchException
-//    {
-//        BooleanQuery query = new BooleanQuery();
-//        query.add( new TermQuery( new Term( StandardIndexRecordFields.GROUPID_EXACT, groupId ) ),
-//                   BooleanClause.Occur.MUST );
-//        query.add( new TermQuery( new Term( StandardIndexRecordFields.ARTIFACTID_EXACT, artifactId ) ),
-//                   BooleanClause.Occur.MUST );
-//
-//        return searchField( query, StandardIndexRecordFields.VERSION );
-//    }
-
-//    private List searchField( org.apache.lucene.search.Query luceneQuery, String fieldName )
-//        throws RepositoryIndexSearchException
-//    {
-//        Set results = new LinkedHashSet();
-//
-//        IndexSearcher searcher;
-//        try
-//        {
-//            searcher = new IndexSearcher( indexLocation.getAbsolutePath() );
-//        }
-//        catch ( IOException e )
-//        {
-//            throw new RepositoryIndexSearchException( "Unable to open index: " + e.getMessage(), e );
-//        }
-//
-//        try
-//        {
-//            Hits hits = searcher.search( luceneQuery );
-//            for ( int i = 0; i < hits.length(); i++ )
-//            {
-//                Document doc = hits.doc( i );
-//
-//                results.add( doc.get( fieldName ) );
-//            }
-//        }
-//        catch ( IOException e )
-//        {
-//            throw new RepositoryIndexSearchException( "Unable to search index: " + e.getMessage(), e );
-//        }
-//        finally
-//        {
-//            closeQuietly( searcher );
-//        }
-//        return new ArrayList( results );
-//    }
-
-    public boolean exists() throws RepositoryIndexException
+    //    public List getAllGroupIds() throws RepositoryIndexException
+    //    {
+    //        return getAllFieldValues( StandardIndexRecordFields.GROUPID_EXACT );
+    //    }
+    //
+    //    public List getArtifactIds( String groupId ) throws RepositoryIndexSearchException
+    //    {
+    //        return searchField( new TermQuery( new Term( StandardIndexRecordFields.GROUPID_EXACT, groupId ) ),
+    //                            StandardIndexRecordFields.ARTIFACTID );
+    //    }
+    //
+    //    public List getVersions( String groupId, String artifactId ) throws RepositoryIndexSearchException
+    //    {
+    //        BooleanQuery query = new BooleanQuery();
+    //        query.add( new TermQuery( new Term( StandardIndexRecordFields.GROUPID_EXACT, groupId ) ),
+    //                   BooleanClause.Occur.MUST );
+    //        query.add( new TermQuery( new Term( StandardIndexRecordFields.ARTIFACTID_EXACT, artifactId ) ),
+    //                   BooleanClause.Occur.MUST );
+    //
+    //        return searchField( query, StandardIndexRecordFields.VERSION );
+    //    }
+
+    //    private List searchField( org.apache.lucene.search.Query luceneQuery, String fieldName )
+    //        throws RepositoryIndexSearchException
+    //    {
+    //        Set results = new LinkedHashSet();
+    //
+    //        IndexSearcher searcher;
+    //        try
+    //        {
+    //            searcher = new IndexSearcher( indexLocation.getAbsolutePath() );
+    //        }
+    //        catch ( IOException e )
+    //        {
+    //            throw new RepositoryIndexSearchException( "Unable to open index: " + e.getMessage(), e );
+    //        }
+    //
+    //        try
+    //        {
+    //            Hits hits = searcher.search( luceneQuery );
+    //            for ( int i = 0; i < hits.length(); i++ )
+    //            {
+    //                Document doc = hits.doc( i );
+    //
+    //                results.add( doc.get( fieldName ) );
+    //            }
+    //        }
+    //        catch ( IOException e )
+    //        {
+    //            throw new RepositoryIndexSearchException( "Unable to search index: " + e.getMessage(), e );
+    //        }
+    //        finally
+    //        {
+    //            closeQuietly( searcher );
+    //        }
+    //        return new ArrayList( results );
+    //    }
+
+    public boolean exists()
+        throws RepositoryIndexException
     {
         if ( IndexReader.indexExists( indexLocation ) )
         {
@@ -343,7 +354,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    public List search( Query query ) throws RepositoryIndexSearchException
+    public List search( Query query )
+        throws RepositoryIndexSearchException
     {
         LuceneQuery lQuery = (LuceneQuery) query;
 
@@ -385,7 +397,12 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
 
         return records;
     }
-    
+
+    public QueryParser getQueryParser()
+    {
+        return this.indexHandlers.getQueryParser();
+    }
+
     private static void closeQuietly( IndexSearcher searcher )
     {
         try
@@ -401,7 +418,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    private static void closeQuietly( TermEnum terms ) throws RepositoryIndexException
+    private static void closeQuietly( TermEnum terms )
+        throws RepositoryIndexException
     {
         if ( terms != null )
         {
@@ -416,7 +434,8 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
         }
     }
 
-    private static void closeQuietly( IndexWriter indexWriter ) throws RepositoryIndexException
+    private static void closeQuietly( IndexWriter indexWriter )
+        throws RepositoryIndexException
     {
         try
         {
@@ -466,4 +485,9 @@ public class LuceneRepositoryContentIndex implements RepositoryContentIndex
     {
         return this.indexLocation;
     }
+
+    public String getId()
+    {
+        return this.indexHandlers.getId();
+    }
 }
diff --git a/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java b/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java
new file mode 100644 (file)
index 0000000..c735d4d
--- /dev/null
@@ -0,0 +1,27 @@
+package org.apache.maven.archiva.indexer.search;
+
+/**
+ * Search across repositories for specified term. 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ * @todo add security to not perform search in repositories you don't have access to.
+ */
+public interface CrossRepositorySearch
+{
+    /**
+     * Search for the specific term across all repositories.
+     * 
+     * @param term the term to search for.
+     * @return the results.
+     */
+    public SearchResults searchForTerm( String term );
+
+    /**
+     * Search for the specific MD5 string across all repositories.
+     * 
+     * @param md5 the md5 string to search for.
+     * @return the results.
+     */
+    public SearchResults searchForMd5( String md5 );
+}
diff --git a/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java b/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java
new file mode 100644 (file)
index 0000000..1c90465
--- /dev/null
@@ -0,0 +1,270 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.maven.archiva.configuration.ArchivaConfiguration;
+import org.apache.maven.archiva.configuration.ConfigurationNames;
+import org.apache.maven.archiva.configuration.RepositoryConfiguration;
+import org.apache.maven.archiva.indexer.RepositoryContentIndex;
+import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory;
+import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
+import org.apache.maven.archiva.indexer.bytecode.BytecodeKeys;
+import org.apache.maven.archiva.indexer.filecontent.FileContentKeys;
+import org.apache.maven.archiva.indexer.hashcodes.HashcodesKeys;
+import org.apache.maven.archiva.indexer.lucene.LuceneQuery;
+import org.apache.maven.archiva.model.ArchivaRepository;
+import org.apache.maven.archiva.repository.ArchivaConfigurationAdaptor;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
+import org.codehaus.plexus.registry.Registry;
+import org.codehaus.plexus.registry.RegistryListener;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * DefaultCrossRepositorySearch 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ * @plexus.component role="org.apache.maven.archiva.indexer.search.CrossRepositorySearch" role-hint="default"
+ */
+public class DefaultCrossRepositorySearch
+    extends AbstractLogEnabled
+    implements CrossRepositorySearch, RegistryListener, Initializable
+{
+
+    private static final int UNKNOWN = 0;
+
+    private static final int FILE_CONTENT = 1;
+
+    private static final int BYTECODE = 2;
+
+    private static final int HASHCODE = 3;
+
+    /**
+     * @plexus.requirement role-hint="lucene"
+     */
+    private RepositoryContentIndexFactory indexFactory;
+
+    /**
+     * @plexus.requirement
+     */
+    private ArchivaConfiguration configuration;
+
+    private Map repositoryMap = new HashMap();
+
+    public SearchResults searchForMd5( String md5 )
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public SearchResults searchForTerm( String term )
+    {
+        List indexes = new ArrayList();
+
+        indexes.addAll( getBytecodeIndexes() );
+        indexes.addAll( getFileContentIndexes() );
+        indexes.addAll( getHashcodeIndexes() );
+
+        SearchResults results = new SearchResults();
+
+        results.getRepositories().addAll( this.repositoryMap.values() );
+
+        Iterator it = indexes.iterator();
+        while ( it.hasNext() )
+        {
+            RepositoryContentIndex index = (RepositoryContentIndex) it.next();
+
+            try
+            {
+                QueryParser parser = index.getQueryParser();
+                LuceneQuery query = new LuceneQuery( parser.parse( term ) );
+                List hits = index.search( query );
+
+                switch ( getIndexId( index ) )
+                {
+                    case BYTECODE:
+                        results.getBytecodeHits().addAll( hits );
+                        break;
+                    case FILE_CONTENT:
+                        results.getContentHits().addAll( hits );
+                        break;
+                    case HASHCODE:
+                        results.getHashcodeHits().addAll( hits );
+                        break;
+                }
+            }
+            catch ( ParseException e )
+            {
+                getLogger().warn( "Unable to parse query [" + term + "]: " + e.getMessage(), e );
+            }
+            catch ( RepositoryIndexSearchException e )
+            {
+                getLogger().warn( "Unable to search index [" + index + "] for term [" + term + "]: " + e.getMessage(),
+                                  e );
+            }
+        }
+
+        return results;
+    }
+
+    private int getIndexId( RepositoryContentIndex index )
+    {
+        if ( FileContentKeys.ID.equals( index.getId() ) )
+        {
+            return FILE_CONTENT;
+        }
+
+        if ( BytecodeKeys.ID.equals( index.getId() ) )
+        {
+            return BYTECODE;
+        }
+
+        if ( HashcodesKeys.ID.equals( index.getId() ) )
+        {
+            return HASHCODE;
+        }
+
+        return UNKNOWN;
+    }
+
+    public List getBytecodeIndexes()
+    {
+        List ret = new ArrayList();
+
+        synchronized ( this.repositoryMap )
+        {
+            Iterator it = this.repositoryMap.values().iterator();
+            while ( it.hasNext() )
+            {
+                ArchivaRepository repo = (ArchivaRepository) it.next();
+
+                if ( !isSearchAllowed( repo ) )
+                {
+                    continue;
+                }
+
+                ret.add( indexFactory.createBytecodeIndex( repo ) );
+            }
+        }
+
+        return ret;
+    }
+
+    public List getFileContentIndexes()
+    {
+        List ret = new ArrayList();
+
+        synchronized ( this.repositoryMap )
+        {
+            Iterator it = this.repositoryMap.values().iterator();
+            while ( it.hasNext() )
+            {
+                ArchivaRepository repo = (ArchivaRepository) it.next();
+
+                if ( !isSearchAllowed( repo ) )
+                {
+                    continue;
+                }
+
+                ret.add( indexFactory.createFileContentIndex( repo ) );
+            }
+        }
+
+        return ret;
+    }
+
+    public List getHashcodeIndexes()
+    {
+        List ret = new ArrayList();
+
+        synchronized ( this.repositoryMap )
+        {
+            Iterator it = this.repositoryMap.values().iterator();
+            while ( it.hasNext() )
+            {
+                ArchivaRepository repo = (ArchivaRepository) it.next();
+
+                if ( !isSearchAllowed( repo ) )
+                {
+                    continue;
+                }
+
+                ret.add( indexFactory.createHashcodeIndex( repo ) );
+            }
+        }
+
+        return ret;
+    }
+
+    public boolean isSearchAllowed( ArchivaRepository repo )
+    {
+        // TODO: test if user has permissions to search in this repo.
+
+        return true;
+    }
+
+    public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
+    {
+        if ( ConfigurationNames.isRepositories( propertyName ) )
+        {
+            initRepositoryMap();
+        }
+    }
+
+    public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
+    {
+        /* Nothing to do here */
+    }
+
+    private void initRepositoryMap()
+    {
+        synchronized ( this.repositoryMap )
+        {
+            this.repositoryMap.clear();
+
+            Iterator it = configuration.getConfiguration().createRepositoryMap().entrySet().iterator();
+            while ( it.hasNext() )
+            {
+                Map.Entry entry = (Entry) it.next();
+                String key = (String) entry.getKey();
+                RepositoryConfiguration repoConfig = (RepositoryConfiguration) entry.getValue();
+                ArchivaRepository repository = ArchivaConfigurationAdaptor.toArchivaRepository( repoConfig );
+                this.repositoryMap.put( key, repository );
+            }
+        }
+    }
+
+    public void initialize()
+        throws InitializationException
+    {
+        initRepositoryMap();
+        configuration.addChangeListener( this );
+    }
+}
diff --git a/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java b/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java
new file mode 100644 (file)
index 0000000..3062f04
--- /dev/null
@@ -0,0 +1,90 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+/**
+ * SearchResults 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class SearchResults
+{
+    private List repositories = new ArrayList();
+
+    private List contentHits = new ArrayList();
+
+    private List bytecodeHits = new ArrayList();
+
+    private List hashcodeHits = new ArrayList();
+
+    public SearchResults()
+    {
+        /* do nothing */
+    }
+
+    public boolean isEmpty()
+    {
+        return ( bytecodeHits.isEmpty() && hashcodeHits.isEmpty() && contentHits.isEmpty() );
+    }
+
+    public List getBytecodeHits()
+    {
+        return bytecodeHits;
+    }
+
+    public List getContentHits()
+    {
+        return contentHits;
+    }
+
+    public List getHashcodeHits()
+    {
+        return hashcodeHits;
+    }
+
+    public List getRepositories()
+    {
+        return repositories;
+    }
+
+    public void setBytecodeHits( List bytecodeHits )
+    {
+        this.bytecodeHits = bytecodeHits;
+    }
+
+    public void setContentHits( List contentHits )
+    {
+        this.contentHits = contentHits;
+    }
+
+    public void setHashcodeHits( List hashcodeHits )
+    {
+        this.hashcodeHits = hashcodeHits;
+    }
+
+    public void setRepositories( List repositories )
+    {
+        this.repositories = repositories;
+    }
+}
index 709f544fe01a36ca751913b7ad3aeab2a5f3a58a..62b7b1b6f90683f2aee7d766c37068eb02ea71bf 100644 (file)
@@ -46,7 +46,8 @@ import java.util.Map;
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
-public abstract class AbstractIndexerTestCase extends PlexusTestCase
+public abstract class AbstractIndexerTestCase
+    extends PlexusTestCase
 {
     protected RepositoryContentIndex index;
 
@@ -72,14 +73,32 @@ public abstract class AbstractIndexerTestCase extends PlexusTestCase
 
     public abstract LuceneIndexHandlers getIndexHandler();
 
-    protected void setUp() throws Exception
+    protected void setUp()
+        throws Exception
     {
         super.setUp();
 
-        RepositoryContentIndexFactory indexFactory =
-            (RepositoryContentIndexFactory) lookup( RepositoryContentIndexFactory.class.getName(), "lucene" );
+        RepositoryContentIndexFactory indexFactory = (RepositoryContentIndexFactory) lookup(
+                                                                                             RepositoryContentIndexFactory.class
+                                                                                                 .getName(), "lucene" );
 
+        ArchivaRepository repository = createTestIndex( getIndexName() );
+
+        index = createIndex( indexFactory, repository );
+
+        indexHandlers = getIndexHandler();
+    }
+
+    private ArchivaRepository createTestIndex( String indexName )
+        throws Exception, IOException
+    {
         File repoDir = new File( getBasedir(), "src/test/managed-repository" );
+        File testIndexesDir = new File( getBasedir(), "target/test-indexes" );
+
+        if ( !testIndexesDir.exists() )
+        {
+            testIndexesDir.mkdirs();
+        }
 
         assertTrue( "Default Test Repository should exist.", repoDir.exists() && repoDir.isDirectory() );
 
@@ -87,7 +106,7 @@ public abstract class AbstractIndexerTestCase extends PlexusTestCase
 
         ArchivaRepository repository = new ArchivaRepository( "testDefaultRepo", "Test Default Repository", repoUri );
 
-        File indexLocation = new File( "target/index-" + getIndexName() + "-" + getName() + "/" );
+        File indexLocation = new File( testIndexesDir, "/index-" + indexName + "-" + getName() + "/" );
 
         MockConfiguration config = (MockConfiguration) lookup( ArchivaConfiguration.class.getName(), "mock" );
 
@@ -103,10 +122,7 @@ public abstract class AbstractIndexerTestCase extends PlexusTestCase
         }
 
         config.getConfiguration().addRepository( repoConfig );
-
-        index = createIndex( indexFactory, repository );
-
-        indexHandlers = getIndexHandler();
+        return repository;
     }
 
     protected Map getArchivaArtifactDumpMap()
@@ -189,12 +205,14 @@ public abstract class AbstractIndexerTestCase extends PlexusTestCase
         return artifact;
     }
 
-    protected void createEmptyIndex() throws IOException
+    protected void createEmptyIndex()
+        throws IOException
     {
         createIndex( Collections.EMPTY_LIST );
     }
 
-    protected void createIndex( List documents ) throws IOException
+    protected void createIndex( List documents )
+        throws IOException
     {
         IndexWriter writer = new IndexWriter( index.getIndexDirectory(), indexHandlers.getAnalyzer(), true );
         for ( Iterator i = documents.iterator(); i.hasNext(); )
index c856f54993fff8893359a9cfc5a345af1ec3944a..249b4d7aed56ee20492a2a2bd60da82345aed457 100644 (file)
@@ -21,8 +21,14 @@ package org.apache.maven.archiva.indexer;
 
 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
 import org.apache.maven.archiva.configuration.Configuration;
+import org.codehaus.plexus.registry.Registry;
 import org.codehaus.plexus.registry.RegistryException;
 import org.codehaus.plexus.registry.RegistryListener;
+import org.easymock.MockControl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * MockConfiguration 
@@ -37,9 +43,21 @@ public class MockConfiguration implements ArchivaConfiguration
 {
     private Configuration configuration = new Configuration();
 
+    private List listeners = new ArrayList();
+
+    private MockControl registryControl;
+
+    private Registry registryMock;
+
+    public MockConfiguration()
+    {
+        registryControl = MockControl.createNiceControl( Registry.class );
+        registryMock = (Registry) registryControl.getMock();
+    }
+
     public void addChangeListener( RegistryListener listener )
     {
-        /* do nothing */
+        listeners.add( listener );
     }
 
     public Configuration getConfiguration()
@@ -47,8 +65,26 @@ public class MockConfiguration implements ArchivaConfiguration
         return configuration;
     }
 
-    public void save( Configuration configuration ) throws RegistryException
+    public void save( Configuration configuration )
+        throws RegistryException
     {
         /* do nothing */
     }
+
+    public void triggerChange( String name, String value )
+    {
+        Iterator it = listeners.iterator();
+        while ( it.hasNext() )
+        {
+            RegistryListener listener = (RegistryListener) it.next();
+            try
+            {
+                listener.afterConfigurationChange( registryMock, name, value );
+            }
+            catch ( Exception e )
+            {
+                e.printStackTrace();
+            }
+        }
+    }
 }
diff --git a/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/BytecodeIndexPopulator.java b/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/BytecodeIndexPopulator.java
new file mode 100644 (file)
index 0000000..f79f115
--- /dev/null
@@ -0,0 +1,141 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.maven.archiva.indexer.bytecode.BytecodeRecord;
+import org.apache.maven.archiva.indexer.bytecode.BytecodeRecordLoader;
+import org.apache.maven.archiva.model.ArchivaArtifact;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * BytecodeIndexPopulator 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class BytecodeIndexPopulator
+    implements IndexPopulator
+{
+
+    public Map getObjectMap()
+    {
+
+        Map dumps = new HashMap();
+
+        // archiva-common-1.0.jar.txt
+        dumps.put( "archiva-common", createArchivaArtifact( "org.apache.maven.archiva", "archiva-common", "1.0", "",
+                                                            "jar" ) );
+
+        // continuum-webapp-1.0.3-SNAPSHOT.war.txt
+        dumps.put( "continuum-webapp", createArchivaArtifact( "org.apache.maven.continuum", "continuum-webapp",
+                                                              "1.0.3-SNAPSHOT", "", "war" ) );
+
+        // daytrader-ear-1.1.ear.txt
+        dumps.put( "daytrader-ear", createArchivaArtifact( "org.apache.geronimo", "daytrader-ear", "1.1", "", "ear" ) );
+
+        // maven-archetype-simple-1.0-alpha-4.jar.txt
+        dumps.put( "maven-archetype-simple", createArchivaArtifact( "org.apache.maven", "maven-archetype-simple",
+                                                                    "1.0-alpha-4", "", "maven-archetype" ) );
+
+        // maven-help-plugin-2.0.2-20070119.121239-2.jar.txt
+        dumps.put( "maven-help-plugin", createArchivaArtifact( "org.apache.maven.plugins", "maven-help-plugin",
+                                                               "2.0.2-20070119.121239-2", "", "maven-plugin" ) );
+
+        // redback-authorization-open-1.0-alpha-1-SNAPSHOT.jar.txt
+        dumps.put( "redback-authorization-open", createArchivaArtifact( "org.codehaus.plexus.redback",
+                                                                        "redback-authorization-open",
+                                                                        "1.0-alpha-1-SNAPSHOT", "", "jar" ) );
+
+        // testng-5.1-jdk15.jar.txt
+        dumps.put( "testng", createArchivaArtifact( "org.testng", "testng", "5.1", "jdk15", "jar" ) );
+
+        // wagon-provider-api-1.0-beta-3-20070209.213958-2.jar.txt
+        dumps.put( "wagon-provider-api", createArchivaArtifact( "org.apache.maven.wagon", "wagon-provider-api",
+                                                                "1.0-beta-3-20070209.213958-2", "", "jar" ) );
+
+        return dumps;
+
+    }
+
+    private ArchivaArtifact createArchivaArtifact( String groupId, String artifactId, String version,
+                                                   String classifier, String type )
+    {
+        ArchivaArtifact artifact = new ArchivaArtifact( groupId, artifactId, version, classifier, type );
+        return artifact;
+    }
+
+    public Map populate( File basedir )
+    {
+        Map records = new HashMap();
+
+        Map artifactDumps = getObjectMap();
+        for ( Iterator iter = artifactDumps.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            ArchivaArtifact artifact = (ArchivaArtifact) entry.getValue();
+            File dumpFile = getDumpFile( basedir, artifact );
+            BytecodeRecord record = BytecodeRecordLoader.loadRecord( dumpFile, artifact );
+            records.put( entry.getKey(), record );
+        }
+
+        return records;
+    }
+
+    protected File getDumpFile( File basedir, ArchivaArtifact artifact )
+    {
+        File dumpDir = new File( basedir, "src/test/artifact-dumps" );
+        StringBuffer filename = new StringBuffer();
+
+        filename.append( artifact.getArtifactId() ).append( "-" ).append( artifact.getVersion() );
+
+        if ( artifact.hasClassifier() )
+        {
+            filename.append( "-" ).append( artifact.getClassifier() );
+        }
+
+        filename.append( "." );
+
+        // TODO: use the ArtifactExtensionMapping object!
+        if ( "maven-plugin".equals( artifact.getType() ) || "maven-archetype".equals( artifact.getType() ) )
+        {
+            filename.append( "jar" );
+        }
+        else
+        {
+            filename.append( artifact.getType() );
+        }
+        filename.append( ".txt" );
+
+        File dumpFile = new File( dumpDir, filename.toString() );
+
+        if ( !dumpFile.exists() )
+        {
+            throw new AssertionFailedError( "Dump file " + dumpFile.getAbsolutePath() + " does not exist (should it?)." );
+        }
+
+        return dumpFile;
+    }
+}
diff --git a/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java b/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java
new file mode 100644 (file)
index 0000000..821ba57
--- /dev/null
@@ -0,0 +1,131 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.lang.StringUtils;
+import org.apache.maven.archiva.configuration.ArchivaConfiguration;
+import org.apache.maven.archiva.configuration.RepositoryConfiguration;
+import org.apache.maven.archiva.indexer.MockConfiguration;
+import org.apache.maven.archiva.indexer.RepositoryContentIndex;
+import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory;
+import org.apache.maven.archiva.model.ArchivaRepository;
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.FileUtils;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * DefaultCrossRepositorySearchTest 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class DefaultCrossRepositorySearchTest
+    extends PlexusTestCase
+{
+
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        RepositoryContentIndexFactory indexFactory = (RepositoryContentIndexFactory) lookup(
+                                                                                             RepositoryContentIndexFactory.class
+                                                                                                 .getName(), "lucene" );
+
+        File repoDir = new File( getBasedir(), "src/test/managed-repository" );
+
+        assertTrue( "Default Test Repository should exist.", repoDir.exists() && repoDir.isDirectory() );
+
+        String repoUri = "file://" + StringUtils.replace( repoDir.getAbsolutePath(), "\\", "/" );
+
+        ArchivaRepository repository = new ArchivaRepository( "testDefaultRepo", "Test Default Repository", repoUri );
+
+        File indexLocation = new File( "target/index-crossrepo-" + getName() + "/" );
+
+        MockConfiguration config = (MockConfiguration) lookup( ArchivaConfiguration.class.getName(), "mock" );
+
+        RepositoryConfiguration repoConfig = new RepositoryConfiguration();
+        repoConfig.setId( repository.getId() );
+        repoConfig.setName( repository.getModel().getName() );
+        repoConfig.setUrl( repository.getModel().getUrl() );
+        repoConfig.setIndexDir( indexLocation.getAbsolutePath() );
+
+        if ( indexLocation.exists() )
+        {
+            FileUtils.deleteDirectory( indexLocation );
+        }
+
+        config.getConfiguration().addRepository( repoConfig );
+
+        // Create the (empty) indexes.
+        RepositoryContentIndex indexHashcode = indexFactory.createHashcodeIndex( repository );
+        RepositoryContentIndex indexBytecode = indexFactory.createBytecodeIndex( repository );
+        RepositoryContentIndex indexContents = indexFactory.createFileContentIndex( repository );
+
+        // Now populate them.
+        Map hashcodesMap = ( new HashcodesIndexPopulator() ).populate( new File( getBasedir() ) );
+        indexHashcode.indexRecords( hashcodesMap.values() );
+        Map bytecodeMap = ( new BytecodeIndexPopulator() ).populate( new File( getBasedir() ) );
+        indexBytecode.indexRecords( bytecodeMap.values() );
+        Map contentMap = ( new FileContentIndexPopulator() ).populate( new File( getBasedir() ) );
+        indexContents.indexRecords( contentMap.values() );
+    }
+
+    private CrossRepositorySearch lookupCrossRepositorySearch()
+        throws Exception
+    {
+        CrossRepositorySearch search = (CrossRepositorySearch) lookup( CrossRepositorySearch.class.getName(), "default" );
+        assertNotNull( "CrossRepositorySearch:default should not be null.", search );
+        return search;
+    }
+
+    public void testSearchTerm()
+        throws Exception
+    {
+        CrossRepositorySearch search = lookupCrossRepositorySearch();
+
+        SearchResults results = search.searchForTerm( "org" );
+        assertHitCounts( 1, 8, 8, 1, results );
+
+        results = search.searchForTerm( "junit" );
+        assertHitCounts( 1, 1, 0, 1, results );
+        
+        results = search.searchForTerm( "monosodium" );
+        assertHitCounts( 1, 0, 0, 0, results );
+    }
+
+    private void assertHitCounts( int repoCount, int bytecodeCount, int hashcodeCount, int contentCount,
+                                  SearchResults results )
+    {
+        assertNotNull( "Search Results should not be null.", results );
+        assertEquals( "Repository Hits", repoCount, results.getRepositories().size() );
+
+        if ( ( bytecodeCount != results.getBytecodeHits().size() )
+            || ( hashcodeCount != results.getHashcodeHits().size() )
+            || ( contentCount != results.getContentHits().size() ) )
+        {
+            fail( "Failed to get expected results hit count.  Expected: (bytecode,hashcode,content) <" + bytecodeCount
+                + "," + hashcodeCount + "," + contentCount + ">, but got <" + results.getBytecodeHits().size() + ","
+                + results.getHashcodeHits().size() + "," + results.getContentHits().size() + "> instead." );
+        }
+    }
+}
diff --git a/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/FileContentIndexPopulator.java b/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/FileContentIndexPopulator.java
new file mode 100644 (file)
index 0000000..9a8bdb4
--- /dev/null
@@ -0,0 +1,82 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.io.FileUtils;
+import org.apache.maven.archiva.indexer.filecontent.FileContentRecord;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * FileContentIndexPopulator 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class FileContentIndexPopulator
+    implements IndexPopulator
+{
+    public Map getObjectMap()
+    {
+        return null;
+    }
+
+    public Map populate( File basedir )
+    {
+        Map map = new HashMap();
+
+        File repoDir = new File( basedir, "src/test/managed-repository" );
+
+        map.put( "parent-pom-1",
+                 createFileContentRecord( repoDir, "org/apache/maven/archiva/record/parent-pom/1/parent-pom-1.pom" ) );
+
+        return map;
+    }
+
+    private FileContentRecord createFileContentRecord( File repoDir, String path )
+    {
+        File pathToFile = new File( repoDir, path );
+
+        if ( !pathToFile.exists() )
+        {
+            throw new AssertionFailedError( "Can't find test file: " + pathToFile.getAbsolutePath() );
+        }
+
+        FileContentRecord record = new FileContentRecord();
+        record.setFile( pathToFile );
+
+        try
+        {
+            record.setContents( FileUtils.readFileToString( pathToFile, null ) );
+        }
+        catch ( IOException e )
+        {
+            e.printStackTrace();
+            throw new AssertionFailedError( "Can't load test file contents: " + pathToFile.getAbsolutePath() );
+        }
+
+        return record;
+    }
+}
diff --git a/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/HashcodesIndexPopulator.java b/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/HashcodesIndexPopulator.java
new file mode 100644 (file)
index 0000000..f61ce86
--- /dev/null
@@ -0,0 +1,114 @@
+package org.apache.maven.archiva.indexer.search;
+
+import org.apache.maven.archiva.indexer.hashcodes.HashcodesRecord;
+import org.apache.maven.archiva.indexer.hashcodes.HashcodesRecordLoader;
+import org.apache.maven.archiva.model.ArchivaArtifact;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+public class HashcodesIndexPopulator
+    implements IndexPopulator
+{
+
+    public Map getObjectMap()
+    {
+        Map dumps = new HashMap();
+
+        // archiva-common-1.0.jar.txt
+        dumps.put( "archiva-common", createArchivaArtifact( "org.apache.maven.archiva", "archiva-common", "1.0", "",
+                                                            "jar" ) );
+
+        // continuum-webapp-1.0.3-SNAPSHOT.war.txt
+        dumps.put( "continuum-webapp", createArchivaArtifact( "org.apache.maven.continuum", "continuum-webapp",
+                                                              "1.0.3-SNAPSHOT", "", "war" ) );
+
+        // daytrader-ear-1.1.ear.txt
+        dumps.put( "daytrader-ear", createArchivaArtifact( "org.apache.geronimo", "daytrader-ear", "1.1", "", "ear" ) );
+
+        // maven-archetype-simple-1.0-alpha-4.jar.txt
+        dumps.put( "maven-archetype-simple", createArchivaArtifact( "org.apache.maven", "maven-archetype-simple",
+                                                                    "1.0-alpha-4", "", "maven-archetype" ) );
+
+        // maven-help-plugin-2.0.2-20070119.121239-2.jar.txt
+        dumps.put( "maven-help-plugin", createArchivaArtifact( "org.apache.maven.plugins", "maven-help-plugin",
+                                                               "2.0.2-20070119.121239-2", "", "maven-plugin" ) );
+
+        // redback-authorization-open-1.0-alpha-1-SNAPSHOT.jar.txt
+        dumps.put( "redback-authorization-open", createArchivaArtifact( "org.codehaus.plexus.redback",
+                                                                        "redback-authorization-open",
+                                                                        "1.0-alpha-1-SNAPSHOT", "", "jar" ) );
+
+        // testng-5.1-jdk15.jar.txt
+        dumps.put( "testng", createArchivaArtifact( "org.testng", "testng", "5.1", "jdk15", "jar" ) );
+
+        // wagon-provider-api-1.0-beta-3-20070209.213958-2.jar.txt
+        dumps.put( "wagon-provider-api", createArchivaArtifact( "org.apache.maven.wagon", "wagon-provider-api",
+                                                                "1.0-beta-3-20070209.213958-2", "", "jar" ) );
+
+        return dumps;
+    }
+
+    public Map populate( File basedir )
+    {
+        Map records = new HashMap();
+
+        Map artifactDumps = getObjectMap();
+        for ( Iterator iter = artifactDumps.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            ArchivaArtifact artifact = (ArchivaArtifact) entry.getValue();
+            File dumpFile = getDumpFile( basedir, artifact );
+            HashcodesRecord record = HashcodesRecordLoader.loadRecord( dumpFile, artifact );
+            records.put( entry.getKey(), record );
+        }
+
+        return records;
+    }
+
+    protected File getDumpFile( File basedir, ArchivaArtifact artifact )
+    {
+        File dumpDir = new File( basedir, "src/test/artifact-dumps" );
+        StringBuffer filename = new StringBuffer();
+
+        filename.append( artifact.getArtifactId() ).append( "-" ).append( artifact.getVersion() );
+
+        if ( artifact.hasClassifier() )
+        {
+            filename.append( "-" ).append( artifact.getClassifier() );
+        }
+
+        filename.append( "." );
+
+        // TODO: use the ArtifactExtensionMapping object!
+        if ( "maven-plugin".equals( artifact.getType() ) || "maven-archetype".equals( artifact.getType() ) )
+        {
+            filename.append( "jar" );
+        }
+        else
+        {
+            filename.append( artifact.getType() );
+        }
+        filename.append( ".txt" );
+
+        File dumpFile = new File( dumpDir, filename.toString() );
+
+        if ( !dumpFile.exists() )
+        {
+            throw new AssertionFailedError( "Dump file " + dumpFile.getAbsolutePath() + " does not exist (should it?)." );
+        }
+
+        return dumpFile;
+    }
+
+    private ArchivaArtifact createArchivaArtifact( String groupId, String artifactId, String version,
+                                                   String classifier, String type )
+    {
+        ArchivaArtifact artifact = new ArchivaArtifact( groupId, artifactId, version, classifier, type );
+        return artifact;
+    }
+}
diff --git a/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/IndexPopulator.java b/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/IndexPopulator.java
new file mode 100644 (file)
index 0000000..6689739
--- /dev/null
@@ -0,0 +1,36 @@
+package org.apache.maven.archiva.indexer.search;
+
+/*
+ * 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.util.Map;
+
+/**
+ * IndexPopulator 
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public interface IndexPopulator
+{
+    public Map getObjectMap();
+
+    public Map populate( File basedir );
+}
diff --git a/archiva-base/archiva-indexer/src/test/resources/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.xml b/archiva-base/archiva-indexer/src/test/resources/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.xml
new file mode 100644 (file)
index 0000000..61a859c
--- /dev/null
@@ -0,0 +1,40 @@
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+      <role-hint>mock</role-hint>
+      <implementation>org.apache.maven.archiva.indexer.MockConfiguration</implementation>
+    </component>
+    <component>
+      <role>org.apache.maven.archiva.indexer.RepositoryContentIndexFactory</role>
+      <role-hint>lucene</role-hint>
+      <implementation>org.apache.maven.archiva.indexer.lucene.LuceneRepositoryContentIndexFactory</implementation>
+      <description>Factory for Lucene repository content index instances.</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+          <role-hint>mock</role-hint>
+          <field-name>configuration</field-name>
+        </requirement>
+      </requirements>
+    </component>
+    <component>
+      <role>org.apache.maven.archiva.indexer.search.CrossRepositorySearch</role>
+      <role-hint>default</role-hint>
+      <implementation>org.apache.maven.archiva.indexer.search.DefaultCrossRepositorySearch</implementation>
+      <description>DefaultCrossRepositorySearch</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.archiva.indexer.RepositoryContentIndexFactory</role>
+          <role-hint>lucene</role-hint>
+          <field-name>indexFactory</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+          <role-hint>mock</role-hint>
+          <field-name>configuration</field-name>
+        </requirement>
+      </requirements>
+    </component>
+  </components>
+</component-set>
index f356f6cfc180c12f810da81108700fa3242ccefe..54ae70edbee92fad5e07ce80c9a3e7b29e1f4878 100644 (file)
@@ -19,26 +19,14 @@ package org.apache.maven.archiva.web.action;
  * under the License.
  */
 
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryParser.MultiFieldQueryParser;
 import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.search.TermQuery;
-import org.apache.maven.archiva.configuration.ArchivaConfiguration;
-import org.apache.maven.archiva.configuration.Configuration;
-import org.apache.maven.archiva.configuration.ConfiguredRepositoryFactory;
-import org.apache.maven.archiva.indexer.RepositoryArtifactIndex;
-import org.apache.maven.archiva.indexer.RepositoryArtifactIndexFactory;
 import org.apache.maven.archiva.indexer.RepositoryIndexException;
 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
-import org.apache.maven.archiva.indexer.lucene.LuceneQuery;
-import org.apache.maven.archiva.indexer.lucene.LuceneRepositoryArtifactIndex;
-import org.apache.maven.archiva.indexer.record.StandardIndexRecordFields;
-import org.apache.maven.archiva.web.util.VersionMerger;
+import org.apache.maven.archiva.indexer.search.CrossRepositorySearch;
+import org.apache.maven.archiva.indexer.search.SearchResults;
 import org.codehaus.plexus.xwork.action.PlexusActionSupport;
 
-import java.io.File;
 import java.net.MalformedURLException;
-import java.util.Collection;
 
 /**
  * Search all indexed fields by the given criteria.
@@ -57,62 +45,48 @@ public class SearchAction
      * The MD5 to search by.
      */
     private String md5;
-
-    /**
-     * Search results.
-     */
-    private Collection searchResults;
-
+    
     /**
-     * @plexus.requirement
+     * The Search Results.
      */
-    private RepositoryArtifactIndexFactory factory;
+    private SearchResults results;
 
     /**
-     * @plexus.requirement
+     * @plexus.requirement role-hint="default"
      */
-    private ConfiguredRepositoryFactory repositoryFactory;
-
-    /**
-     * @plexus.requirement
-     */
-    private ArchivaConfiguration archivaConfiguration;
+    private CrossRepositorySearch crossRepoSearch;
 
     private static final String RESULTS = "results";
 
     private static final String ARTIFACT = "artifact";
 
-    private String infoMessage;
-
     public String quickSearch()
         throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException, ParseException
     {
-        // TODO: give action message if indexing is in progress
+        /* TODO: give action message if indexing is in progress.
+         * This should be based off a count of 'unprocessed' artifacts.
+         * This (yet to be written) routine could tell the user that X artifacts are not yet 
+         * present in the full text search.
+         */
 
         assert q != null && q.length() != 0;
 
-        RepositoryArtifactIndex index = getIndex();
-
-        if ( !index.exists() )
-        {
-            addActionError( "The repository is not yet indexed. Please wait, and then try again." );
-            return ERROR;
-        }
-
-        MultiFieldQueryParser parser = new MultiFieldQueryParser( new String[]{StandardIndexRecordFields.GROUPID,
-            StandardIndexRecordFields.ARTIFACTID, StandardIndexRecordFields.BASE_VERSION,
-            StandardIndexRecordFields.CLASSIFIER, StandardIndexRecordFields.CLASSES, StandardIndexRecordFields.FILES,
-            StandardIndexRecordFields.TYPE, StandardIndexRecordFields.PROJECT_NAME,
-            StandardIndexRecordFields.PROJECT_DESCRIPTION}, LuceneRepositoryArtifactIndex.getAnalyzer() );
-        searchResults = index.search( new LuceneQuery( parser.parse( q ) ) );
+        results = crossRepoSearch.searchForTerm( q );
 
-        if ( searchResults.isEmpty() )
+        if ( results.isEmpty() )
         {
             addActionError( "No results found" );
             return INPUT;
         }
 
-        searchResults = VersionMerger.merge( searchResults );
+        // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
+        
+        /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in 
+         * the future to return relevant links better.
+         * I expect the lucene scoring system to take multiple hits on different areas of a single document
+         * to result in a higher score. 
+         *   - Joakim
+         */
 
         return SUCCESS;
     }
@@ -124,23 +98,15 @@ public class SearchAction
 
         assert md5 != null && md5.length() != 0;
 
-        RepositoryArtifactIndex index = getIndex();
-
-        if ( !index.exists() )
-        {
-            addActionError( "The repository is not yet indexed. Please wait, and then try again." );
-            return ERROR;
-        }
-
-        searchResults = index.search(
-            new LuceneQuery( new TermQuery( new Term( StandardIndexRecordFields.MD5, md5.toLowerCase() ) ) ) );
-
-        if ( searchResults.isEmpty() )
+        results = crossRepoSearch.searchForMd5( q );
+        
+        if ( results.isEmpty() )
         {
             addActionError( "No results found" );
             return INPUT;
         }
-        if ( searchResults.size() == 1 )
+        
+        if ( results.getHashcodeHits().size() == 1 )
         {
             return ARTIFACT;
         }
@@ -150,15 +116,6 @@ public class SearchAction
         }
     }
 
-    private RepositoryArtifactIndex getIndex()
-        throws RepositoryIndexException
-    {
-        Configuration configuration = archivaConfiguration.getConfiguration();
-        File indexPath = new File( configuration.getIndexPath() );
-
-        return factory.createStandardIndex( indexPath );
-    }
-
     public String doInput()
     {
         return INPUT;
@@ -183,19 +140,4 @@ public class SearchAction
     {
         this.md5 = md5;
     }
-
-    public Collection getSearchResults()
-    {
-        return searchResults;
-    }
-
-    public String getInfoMessage()
-    {
-        return infoMessage;
-    }
-
-    public void setInfoMessage( String infoMessage )
-    {
-        this.infoMessage = infoMessage;
-    }
 }