aboutsummaryrefslogtreecommitdiffstats
path: root/archiva-modules
diff options
context:
space:
mode:
Diffstat (limited to 'archiva-modules')
-rw-r--r--archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java17
-rw-r--r--archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java107
-rw-r--r--archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java56
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java304
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml34
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml10
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp67
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp5
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp183
9 files changed, 671 insertions, 112 deletions
diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java
index 63e21151c..52596ff9f 100644
--- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java
+++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java
@@ -69,4 +69,21 @@ public interface CrossRepositorySearch
* @return the results.
*/
public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits );
+
+ /**
+ * Search for a specific artifact matching the given field values. The search is performed on the bytecode
+ * index/indices.
+ *
+ * @param principal
+ * @param selectedRepos repository to be searched
+ * @param groupId groupId to be matched
+ * @param artifactId artifactId to be matched
+ * @param version version to be matched
+ * @param className Java class or package name to be matched
+ * @param limits the limits to apply to the search results
+ * @return
+ */
+ public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId,
+ String artifactId, String version, String className,
+ SearchResultLimits limits );
}
diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java
index ede311346..a21c4fa6b 100644
--- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java
+++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.document.Document;
+import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
@@ -32,16 +33,20 @@ import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.MultiSearcher;
+import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.Searchable;
+import org.apache.lucene.search.TermQuery;
import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import org.apache.maven.archiva.configuration.ConfigurationNames;
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
+import org.apache.maven.archiva.indexer.ArtifactKeys;
import org.apache.maven.archiva.indexer.RepositoryContentIndex;
import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory;
import org.apache.maven.archiva.indexer.RepositoryIndexException;
import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
import org.apache.maven.archiva.indexer.bytecode.BytecodeHandlers;
+import org.apache.maven.archiva.indexer.bytecode.BytecodeKeys;
import org.apache.maven.archiva.indexer.filecontent.FileContentHandlers;
import org.apache.maven.archiva.indexer.hashcodes.HashcodesHandlers;
import org.apache.maven.archiva.indexer.hashcodes.HashcodesKeys;
@@ -57,7 +62,7 @@ import org.slf4j.LoggerFactory;
/**
* DefaultCrossRepositorySearch
- *
+ *
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
* @version $Id$
* @plexus.component role="org.apache.maven.archiva.indexer.search.CrossRepositorySearch" role-hint="default"
@@ -66,12 +71,12 @@ public class DefaultCrossRepositorySearch
implements CrossRepositorySearch, RegistryListener, Initializable
{
private Logger log = LoggerFactory.getLogger( DefaultCrossRepositorySearch.class );
-
+
/**
* @plexus.requirement role-hint="lucene"
*/
private RepositoryContentIndexFactory indexFactory;
-
+
/**
* @plexus.requirement
*/
@@ -79,14 +84,61 @@ public class DefaultCrossRepositorySearch
private List<ManagedRepositoryConfiguration> localIndexedRepositories = new ArrayList<ManagedRepositoryConfiguration>();
- public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits )
+ public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId,
+ String artifactId, String version, String className,
+ SearchResultLimits limits )
+ {
+ List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos );
+ SearchResults results = new SearchResults();
+ BooleanQuery booleanQuery = new BooleanQuery();
+
+ if ( groupId != null && groupId.length() > 0 )
+ {
+ parseAndAdd( booleanQuery, ArtifactKeys.GROUPID, groupId, "\\.|-" );
+ }
+
+ if ( artifactId != null && artifactId.length() > 0 )
+ {
+ parseAndAdd( booleanQuery, ArtifactKeys.ARTIFACTID, artifactId, "\\.|-" );
+ }
+
+ if ( version != null && version.length() > 0 )
+ {
+ parseAndAdd( booleanQuery, ArtifactKeys.VERSION, version, "\\.|-" );
+ }
+
+ if ( className != null && className.length() > 0 )
+ {
+
+ try
+ {
+ QueryParser parser =
+ new MultiFieldQueryParser( new String[] { BytecodeKeys.CLASSES, BytecodeKeys.FILES,
+ BytecodeKeys.METHODS }, new BytecodeHandlers().getAnalyzer() );
+ booleanQuery.add( parser.parse( className ), BooleanClause.Occur.MUST );
+ }
+ catch ( ParseException e )
+ {
+
+ }
+ }
+
+ LuceneQuery query = new LuceneQuery( booleanQuery );
+ results = searchAll( query, limits, indexes, null );
+ results.getRepositories().add( this.localIndexedRepositories );
+
+ return results;
+ }
+
+ public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum,
+ SearchResultLimits limits )
{
List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos );
try
{
QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1},
- new HashcodesHandlers().getAnalyzer() );
+ new HashcodesHandlers().getAnalyzer() );
LuceneQuery query = new LuceneQuery( parser.parse( checksum ) );
SearchResults results = searchAll( query, limits, indexes, null );
results.getRepositories().addAll( this.localIndexedRepositories );
@@ -124,23 +176,22 @@ public class DefaultCrossRepositorySearch
return new SearchResults();
}
-
public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
{
return searchForTerm( principal, selectedRepos, term, limits, null );
}
-
+
public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term,
SearchResultLimits limits, List<String> previousSearchTerms )
{
List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos );
-
+
try
{
QueryParser parser = new FileContentHandlers().getQueryParser();
LuceneQuery query = null;
SearchResults results = null;
- if( previousSearchTerms == null || previousSearchTerms.isEmpty() )
+ if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
{
query = new LuceneQuery( parser.parse( term ) );
results = searchAll( query, limits, indexes, null );
@@ -149,17 +200,17 @@ public class DefaultCrossRepositorySearch
{
// AND the previous search terms
BooleanQuery booleanQuery = new BooleanQuery();
- for( String previousSearchTerm : previousSearchTerms )
+ for ( String previousSearchTerm : previousSearchTerms )
{
booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST );
}
-
- query = new LuceneQuery( booleanQuery );
+
+ query = new LuceneQuery( booleanQuery );
Filter filter = new QueryWrapperFilter( parser.parse( term ) );
results = searchAll( query, limits, indexes, filter );
- }
+ }
results.getRepositories().addAll( this.localIndexedRepositories );
-
+
return results;
}
catch ( ParseException e )
@@ -168,9 +219,9 @@ public class DefaultCrossRepositorySearch
}
// empty results.
- return new SearchResults();
+ return new SearchResults();
}
-
+
private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter )
{
org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery();
@@ -203,7 +254,7 @@ public class DefaultCrossRepositorySearch
// Perform the search.
Hits hits = null;
- if( filter != null )
+ if ( filter != null )
{
hits = searcher.search( specificQuery, filter );
}
@@ -355,7 +406,7 @@ public class DefaultCrossRepositorySearch
return ret;
}
-
+
private boolean indexExists( RepositoryContentIndex index )
{
try
@@ -402,6 +453,26 @@ public class DefaultCrossRepositorySearch
}
}
+ private void parseAndAdd( BooleanQuery query, String key, String value, String delimiter )
+ {
+ if ( value != null && value.length() > 0 )
+ {
+ String[] terms = value.split( delimiter );
+ for ( int i = 0; i < terms.length; i++ )
+ {
+ Term valueTerm = new Term( key, terms[i] );
+ Query valueQuery = new TermQuery( valueTerm );
+ query.add( valueQuery, BooleanClause.Occur.MUST );
+ }
+ }
+ else
+ {
+ Term valueTerm = new Term( key, value );
+ Query valueQuery = new TermQuery( valueTerm );
+ query.add( valueQuery, BooleanClause.Occur.MUST );
+ }
+ }
+
public void initialize()
throws InitializationException
{
diff --git a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java
index 71c872d67..005e3e719 100644
--- a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java
+++ b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java
@@ -242,6 +242,51 @@ public class DefaultCrossRepositorySearchTest
"org.apache.maven.continuum.web.action.BuildDefinitionAction.isBuildFresh", null, true );
}
+ public void testExecuteFilteredSearch()
+ throws Exception
+ {
+ CrossRepositorySearch search = lookupCrossRepositorySearch();
+
+ String expectedRepos[] = new String[] { TEST_DEFAULT_REPO_ID };
+
+ String expectedResults[] = new String[] { "org1", "org2", "org3", "org4", "org5", "org6", "org7", "org8" };
+
+ String secondExpectedResults[] = new String[] { "continuum-webapp" };
+
+ String thirdExpectedResults[] = new String[] { "archiva-common" };
+
+ // search for groupId
+ assertFilteredSearchResults( expectedRepos, expectedResults, search, "org", null, null, null, 30 );
+
+ // search for groupId and artifactId
+ assertFilteredSearchResults( expectedRepos, secondExpectedResults, search, "org.apache.maven",
+ "continuum-webapp", null, null, 30 );
+
+ // search for groupId , artifactId and version
+ assertFilteredSearchResults( expectedRepos, thirdExpectedResults, search, "org.apache.maven.archiva",
+ "archiva-common", "1.0", null, 30 );
+ }
+
+ private void assertFilteredSearchResults ( String expectedRepos[], String expectedResults[], CrossRepositorySearch search,
+ String groupId, String artifactId, String version, String className , int rowCount )
+ {
+ SearchResultLimits limits = new SearchResultLimits( 0 );
+ limits.setPageSize( rowCount );
+
+ List<String> selectedRepos = new ArrayList<String>();
+ selectedRepos.addAll( Arrays.asList( expectedRepos ) );
+
+ SearchResults results = null;
+
+ results = search.executeFilteredSearch( "guest" , selectedRepos, groupId, artifactId, version, className, limits );
+
+ assertNotNull( "Search Results should not be null.", results );
+ assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() );
+ assertEquals( expectedRepos.length, 1);
+ assertEquals( TEST_DEFAULT_REPO_ID , selectedRepos.get( 0 ) );
+ assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() );
+ }
+
private void assertSearchResults( String expectedRepos[], String expectedResults[], CrossRepositorySearch search,
String term, List<String> previousSearchTerms, boolean bytecode )
throws Exception
@@ -251,11 +296,12 @@ public class DefaultCrossRepositorySearchTest
List<String> selectedRepos = new ArrayList<String>();
selectedRepos.addAll( Arrays.asList( expectedRepos ) );
-
+
SearchResults results = null;
+
if( previousSearchTerms == null )
- {
- if( bytecode )
+ {
+ if( bytecode )
{
results = search.searchForBytecode( "guest", selectedRepos, term, limits );
}
@@ -268,16 +314,18 @@ public class DefaultCrossRepositorySearchTest
{
results = search.searchForTerm( "guest", selectedRepos, term, limits, previousSearchTerms );
}
+
assertNotNull( "Search Results should not be null.", results );
assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() );
+
// TODO: test the repository ids returned.
assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() );
// TODO: test the order of hits.
// TODO: test the value of the hits.
}
-
+
protected ManagedRepositoryConfiguration createRepository( String id, String name, File location )
{
ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java
index 795d3712c..35e770b26 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java
@@ -1,5 +1,5 @@
package org.apache.maven.archiva.web.action;
-
+
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -23,10 +23,12 @@ import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
-import com.opensymphony.xwork.ActionContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
+import org.apache.maven.archiva.configuration.ArchivaConfiguration;
+import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
import org.apache.maven.archiva.database.ArchivaDAO;
import org.apache.maven.archiva.database.Constraint;
import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
@@ -42,6 +44,9 @@ import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
+import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.Preparable;
+
/**
* Search all indexed fields by the given criteria.
*
@@ -49,10 +54,16 @@ import org.codehaus.plexus.xwork.action.PlexusActionSupport;
*/
public class SearchAction
extends PlexusActionSupport
-{
+ implements Preparable
+{
/**
* Query string.
*/
+
+ private ArchivaConfiguration archivaConfiguration;
+
+ private Map<String, ManagedRepositoryConfiguration> managedRepositories;
+
private String q;
/**
@@ -90,27 +101,132 @@ public class SearchAction
private int totalPages;
- private boolean searchResultsOnly;
+ private boolean searchResultsOnly;
private String completeQueryString;
private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
-
+
private static final String BYTECODE_KEYWORD = "bytecode:";
+ private List<String> managedRepositoryList;
+
+ private String groupId;
+
+ private String artifactId;
+
+ private String version;
+
+ private String className;
+
+ private int rowCount = 30;
+
+ private String repositoryId;
+
+ private boolean fromFilterSearch;
+
+ private boolean filterSearch = false;
+
+ private boolean fromResultsPage;
+
+ private int num;
+
+ public boolean isFromResultsPage()
+ {
+ return fromResultsPage;
+ }
+
+ public void setFromResultsPage( boolean fromResultsPage )
+ {
+ this.fromResultsPage = fromResultsPage;
+ }
+
+ public boolean isFromFilterSearch()
+ {
+ return fromFilterSearch;
+ }
+
+ public void setFromFilterSearch( boolean fromFilterSearch )
+ {
+ this.fromFilterSearch = fromFilterSearch;
+ }
+
+ public void prepare()
+ {
+ managedRepositoryList = new ArrayList<String>();
+ managedRepositoryList = getObservableRepos();
+
+ if ( managedRepositoryList.size() > 0 )
+ {
+ managedRepositoryList.add( "all" );
+ }
+ }
+
+ // advanced search MRM-90 -- filtered search
+ public String filteredSearch()
+ throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
+ {
+ fromFilterSearch = true;
+
+ if ( CollectionUtils.isEmpty( managedRepositoryList ) )
+ {
+ return GlobalResults.ACCESS_TO_NO_REPOS;
+ }
+
+ SearchResultLimits limits = new SearchResultLimits( currentPage );
+
+ limits.setPageSize( rowCount );
+ List<String> selectedRepos = new ArrayList<String>();
+
+ if ( repositoryId.equals( "all" ) )
+ {
+ selectedRepos = getObservableRepos();
+ }
+ else
+ {
+ selectedRepos.add( repositoryId );
+ }
+
+ if ( CollectionUtils.isEmpty( selectedRepos ) )
+ {
+ return GlobalResults.ACCESS_TO_NO_REPOS;
+ }
+
+ results =
+ crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version,
+ className, limits );
+
+ if ( results.isEmpty() )
+ {
+ addActionError( "No results found" );
+ return INPUT;
+ }
+
+ totalPages = results.getTotalHits() / limits.getPageSize();
+
+ if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
+ {
+ totalPages = totalPages + 1;
+ }
+
+ return SUCCESS;
+ }
+
public String quickSearch()
throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
{
/* 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 (unprocessed) artifacts are not yet
+ * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
* present in the full text search.
*/
assert q != null && q.length() != 0;
-
+
+ fromFilterSearch = false;
+
SearchResultLimits limits = new SearchResultLimits( currentPage );
-
+
List<String> selectedRepos = getObservableRepos();
if ( CollectionUtils.isEmpty( selectedRepos ) )
{
@@ -118,13 +234,13 @@ public class SearchAction
}
if( isBytecodeSearch( q ) )
- {
+ {
results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, removeKeyword( q ), limits );
}
else
{
if( searchResultsOnly && !completeQueryString.equals( "" ) )
- {
+ {
results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
}
else
@@ -133,33 +249,33 @@ public class SearchAction
results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
}
}
-
+
if ( results.isEmpty() )
{
addActionError( "No results found" );
return INPUT;
}
-
+
totalPages = results.getTotalHits() / limits.getPageSize();
-
+
if( (results.getTotalHits() % limits.getPageSize()) != 0 )
{
totalPages = totalPages + 1;
}
// 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
+ /* 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.
+ * to result in a higher score.
* - Joakim
*/
-
+
if( !isEqualToPreviousSearchTerm( q ) )
{
buildCompleteQueryString( q );
}
-
+
return SUCCESS;
}
@@ -185,10 +301,10 @@ public class SearchAction
if ( databaseResults.size() == 1 )
{
- // 1 hit? return it's information directly!
+ // 1 hit? return it's information directly!
return ARTIFACT;
}
-
+
return RESULTS;
}
@@ -197,12 +313,12 @@ public class SearchAction
{
return INPUT;
}
-
+
private String getPrincipal()
{
return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
-
+
private List<String> getObservableRepos()
{
try
@@ -227,44 +343,44 @@ public class SearchAction
private void buildCompleteQueryString( String searchTerm )
{
- if( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
+ if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
{
searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
}
-
- if( completeQueryString == null || "".equals( completeQueryString ) )
+
+ if ( completeQueryString == null || "".equals( completeQueryString ) )
{
completeQueryString = searchTerm;
}
else
- {
+ {
completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
}
}
-
+
private List<String> parseCompleteQueryString()
{
- List<String> parsedCompleteQueryString = new ArrayList<String>();
+ List<String> parsedCompleteQueryString = new ArrayList<String>();
String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
CollectionUtils.addAll( parsedCompleteQueryString, parsed );
-
+
return parsedCompleteQueryString;
}
-
+
private boolean isEqualToPreviousSearchTerm( String searchTerm )
{
- if( !"".equals( completeQueryString ) )
+ if ( !"".equals( completeQueryString ) )
{
String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
- if( StringUtils.equalsIgnoreCase( searchTerm, parsed[ parsed.length - 1 ] ) )
+ if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
{
return true;
}
}
-
+
return false;
}
-
+
public String getQ()
{
return q;
@@ -284,12 +400,12 @@ public class SearchAction
{
return databaseResults;
}
-
+
public void setCurrentPage( int page )
{
this.currentPage = page;
}
-
+
public int getCurrentPage()
{
return currentPage;
@@ -323,23 +439,123 @@ public class SearchAction
public void setCompleteQueryString( String completeQueryString )
{
this.completeQueryString = completeQueryString;
- }
-
+ }
+
private boolean isBytecodeSearch( String queryString )
{
- if( queryString.startsWith( BYTECODE_KEYWORD ) )
+ if ( queryString.startsWith( BYTECODE_KEYWORD ) )
{
- return true;
+ return true;
}
-
+
return false;
}
-
+
private String removeKeyword( String queryString )
- {
+ {
String qString = StringUtils.uncapitalize( queryString );
- qString= StringUtils.remove( queryString, BYTECODE_KEYWORD );
-
+ qString = StringUtils.remove( queryString, BYTECODE_KEYWORD );
+
return qString;
}
+
+ public ArchivaConfiguration getArchivaConfiguration()
+ {
+ return archivaConfiguration;
+ }
+
+ public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
+ {
+ this.archivaConfiguration = archivaConfiguration;
+ }
+
+ public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
+ {
+ return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
+ }
+
+ public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
+ {
+ this.managedRepositories = managedRepositories;
+ }
+
+ public String getGroupId()
+ {
+ return groupId;
+ }
+
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ public String getArtifactId()
+ {
+ return artifactId;
+ }
+
+ public void setArtifactId( String artifactId )
+ {
+ this.artifactId = artifactId;
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion( String version )
+ {
+ this.version = version;
+ }
+
+ public int getRowCount()
+ {
+ return rowCount;
+ }
+
+ public void setRowCount( int rowCount )
+ {
+ this.rowCount = rowCount;
+ }
+
+ public boolean isFilterSearch()
+ {
+ return filterSearch;
+ }
+
+ public void setFilterSearch( boolean filterSearch )
+ {
+ this.filterSearch = filterSearch;
+ }
+
+ public String getRepositoryId()
+ {
+ return repositoryId;
+ }
+
+ public void setRepositoryId( String repositoryId )
+ {
+ this.repositoryId = repositoryId;
+ }
+
+ public List<String> getManagedRepositoryList()
+ {
+ return managedRepositoryList;
+ }
+
+ public void setManagedRepositoryList( List<String> managedRepositoryList )
+ {
+ this.managedRepositoryList = managedRepositoryList;
+ }
+
+ public String getClassName()
+ {
+ return className;
+ }
+
+ public void setClassName( String className )
+ {
+ this.className = className;
+ }
}
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml
new file mode 100644
index 000000000..5f9c89750
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ ~ 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.
+ -->
+
+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
+ "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
+
+<validators>
+ <field name="rowCount">
+ <field-validator type="int">
+ <message>Invalid entry</message>
+ </field-validator>
+ <field-validator type="int">
+ <param name="min">1</param>
+ <message>Row count must be larger than ${min}.</message>
+ </field-validator>
+ </field>
+</validators>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml
index 88297d58a..36509e11e 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml
@@ -143,6 +143,16 @@
<action name="index" class="searchAction" method="input">
<result name="input">/WEB-INF/jsp/quickSearch.jsp</result>
</action>
+
+ <action name="advancedSearch" class="searchAction" method="input">
+ <result name="input">/WEB-INF/jsp/advancedSearch.jsp</result>
+ </action>
+
+ <action name="filteredSearch" class="searchAction" method="filteredSearch">
+ <result name="input">/WEB-INF/jsp/advancedSearch.jsp</result>
+ <result>/WEB-INF/jsp/results.jsp</result>
+ <result name="error">/WEB-INF/jsp/quickSearch.jsp</result>
+ </action>
<action name="quickSearch" class="searchAction" method="quickSearch">
<result name="input">/WEB-INF/jsp/quickSearch.jsp</result>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp
new file mode 100644
index 000000000..3a87d1f5e
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp
@@ -0,0 +1,67 @@
+<%--
+ ~ 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.
+ --%>
+
+<%@ taglib prefix="ww" uri="/webwork" %>
+<html>
+<head>
+ <title>Advanced Search</title>
+ <ww:head/>
+</head>
+
+<ww:if test="%{infoMessage != null}">
+ <p>${infoMessage}</p>
+</ww:if>
+
+<body>
+
+<h1>Advanced Search</h1>
+
+
+<div id="contentArea">
+
+ <div id="searchBox">
+
+ <ww:form method="get" action="filteredSearch" validate="true">
+ <ww:textfield label="Row Count" size="50" name="rowCount"/>
+ <ww:textfield label="Group Id" size="50" name="groupId"/>
+ <ww:textfield label="Artifact Id" size="50" name="artifactId"/>
+ <ww:textfield label="Version" size="50" name="version"/>
+ <ww:textfield label="Class / Package" size="50" name="className"/>
+ <ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/>
+ <ww:hidden name="completeQueryString" value="${completeQueryString}"/>
+ <ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/>
+ <ww:submit label="Go!"/>
+ </ww:form>
+
+ <ww:url id="indexUrl" action="index"/>
+ <ww:a href="%{indexUrl}">
+ Quick Search Page
+ </ww:a>
+
+ </div>
+
+ <script type="text/javascript">
+ document.getElementById("filteredSearch_groupId").focus();
+ </script>
+ <ww:actionerror/>
+
+</div>
+
+</body>
+</html>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp
index 537bc6c84..45a435e75 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp
@@ -44,6 +44,11 @@
document.getElementById("quickSearch_q").focus();
</script>
+ <ww:url id="filteredSearchUrl" action="advancedSearch"/>
+ <ww:a href="%{filteredSearchUrl}">
+ Advanced Search
+ </ww:a>
+
<p>
<ww:actionerror/>
</p>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp
index 889fa75dd..910494540 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp
@@ -30,7 +30,12 @@
<body>
-<h1>Search</h1>
+<c:if test="${fromFilterSearch == true}">
+ <h1>Advanced Search</h1>
+</c:if>
+<c:if test="${fromFilterSearch == false}">
+ <h1>Search</h1>
+</c:if>
<c:url var="imgNextPageUrl" value="/images/icon_next_page.gif"/>
<c:url var="imgPrevPageUrl" value="/images/icon_prev_page.gif"/>
@@ -39,20 +44,44 @@
<div id="contentArea">
<div id="searchBox">
- <ww:form method="get" action="quickSearch" validate="true">
- <ww:textfield label="Search for" size="50" name="q"/>
- <ww:checkbox label="Search within results" name="searchResultsOnly"/>
- <ww:hidden name="completeQueryString" value="${completeQueryString}"/>
- <ww:submit label="Go!"/>
- </ww:form>
- <script type="text/javascript">
- document.getElementById("quickSearch_q").focus();
- </script>
+ <c:if test="${fromFilterSearch == true}">
+ <ww:form method="get" action="filteredSearch" validate="true">
+ <ww:textfield label="Row Count" size="50" name="rowCount"/>
+ <ww:textfield label="Group Id" size="50" name="groupId"/>
+ <ww:textfield label="Artifact Id" size="50" name="artifactId"/>
+ <ww:textfield label="Version" size="50" name="version"/>
+ <ww:textfield label="Class / Package" size="50" name="className"/>
+ <ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/>
+ <ww:hidden name="completeQueryString" value="${completeQueryString}"/>
+ <ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/>
+ <ww:submit label="Go!"/>
+ </ww:form>
+
+ <ww:url id="indexUrl" action="index"/>
+ <ww:a href="%{indexUrl}">
+ Quick Search Page
+ </ww:a>
+ <script type="text/javascript">
+ document.getElementById("filteredSearch_groupId").focus();
+ </script>
+ </c:if>
+ <c:if test="${fromFilterSearch == false}">
+ <ww:form method="get" action="quickSearch" validate="true">
+ <ww:textfield label="Search for" size="50" name="q"/>
+ <ww:checkbox label="Search within results" name="searchResultsOnly"/>
+ <ww:hidden name="completeQueryString" value="${completeQueryString}"/>
+ <ww:submit label="Go!"/>
+ </ww:form>
+ <script type="text/javascript">
+ document.getElementById("quickSearch_q").focus();
+ </script>
+ </c:if>
<p>
<ww:actionerror/>
</p>
+
</div>
<h1>Results</h1>
@@ -62,10 +91,10 @@
<%-- search was made from the indices --%>
<c:when test="${databaseResults == null}">
- <c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set>
+ <c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set>
<c:choose>
<c:when test="${results.totalHits > results.limits.pageSize}">
- <p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p>
+ <p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p>
</c:when>
<c:otherwise>
<p>Hits: 1 to ${hitsNum} of ${results.totalHits}</p>
@@ -80,22 +109,55 @@
<%-- Pagination start --%>
<p>
<%-- Prev & Next icons --%>
- <c:set var="prevPageUrl">
- <ww:url action="quickSearch" namespace="/">
- <ww:param name="q" value="%{'${q}'}"/>
- <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
- <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
- <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
- </ww:url>
- </c:set>
- <c:set var="nextPageUrl">
- <ww:url action="quickSearch" namespace="/">
- <ww:param name="q" value="%{'${q}'}"/>
- <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
- <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
- <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
- </ww:url>
- </c:set>
+ <c:if test="${fromFilterSearch == false}">
+ <c:set var="prevPageUrl">
+ <ww:url action="quickSearch" namespace="/">
+ <ww:param name="q" value="%{'${q}'}"/>
+ <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
+ </ww:url>
+ </c:set>
+ <c:set var="nextPageUrl">
+ <ww:url action="quickSearch" namespace="/">
+ <ww:param name="q" value="%{'${q}'}"/>
+ <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
+ </ww:url>
+ </c:set>
+ </c:if>
+
+ <c:if test="${fromFilterSearch == true}">
+ <c:set var="prevPageUrl">
+ <ww:url action="filteredSearch" namespace="/">
+ <%-- <ww:param name="q" value="%{'${q}'}"/> --%>
+ <ww:param name="rowCount" value="%{'${rowCount}'}"/>
+ <ww:param name="groupId" value="%{'${groupId}'}"/>
+ <ww:param name="artifactId" value="%{'${artifactId}'}"/>
+ <ww:param name="version" value="%{'${version}'}"/>
+ <ww:param name="className" value="%{'${className}'}"/>
+ <ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
+ <ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
+ <ww:param name="fromResultsPage" value="true"/>
+ <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
+ <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
+ <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
+ </ww:url>
+ </c:set>
+ <c:set var="nextPageUrl">
+ <ww:url action="filteredSearch" namespace="/">
+<%-- <ww:param name="q" value="%{'${q}'}"/> --%>
+ <ww:param name="rowCount" value="%{'${rowCount}'}"/>
+ <ww:param name="groupId" value="%{'${groupId}'}"/>
+ <ww:param name="artifactId" value="%{'${artifactId}'}"/>
+ <ww:param name="version" value="%{'${version}'}"/>
+ <ww:param name="className" value="%{'${className}'}"/>
+ <ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
+ <ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
+ <ww:param name="fromResultsPage" value="true"/>
+ <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
+ <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
+ <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
+ </ww:url>
+ </c:set>
+ </c:if>
<c:choose>
<c:when test="${currentPage == 0}">
@@ -133,25 +195,54 @@
</c:choose>
<c:forEach var="i" begin="${beginVal}" end="${endVal}">
- <c:choose>
- <c:when test="${i != currentPage}">
- <c:set var="specificPageUrl">
- <ww:url action="quickSearch" namespace="/">
- <ww:param name="q" value="%{'${q}'}"/>
- <ww:param name="currentPage" value="%{'${i}'}"/>
- <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
- <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
- </ww:url>
- </c:set>
- <a href="${specificPageUrl}">${i + 1}</a>
- </c:when>
- <c:otherwise>
- <b>${i + 1}</b>
- </c:otherwise>
- </c:choose>
+ <c:if test="${fromFilterSearch == false}">
+ <c:choose>
+ <c:when test="${i != currentPage}">
+ <c:set var="specificPageUrl">
+ <ww:url action="quickSearch" namespace="/">
+ <ww:param name="q" value="%{'${q}'}"/>
+ <ww:param name="currentPage" value="%{'${i}'}"/>
+ <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
+ <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
+ </ww:url>
+ </c:set>
+ <a href="${specificPageUrl}">${i + 1}</a>
+ </c:when>
+ <c:otherwise>
+ <b>${i + 1}</b>
+ </c:otherwise>
+ </c:choose>
+ </c:if>
+
+ <c:if test="${fromFilterSearch == true}">
+ <c:choose>
+ <c:when test="${i != currentPage}">
+ <c:set var="specificPageUrl">
+ <ww:url action="filteredSearch" namespace="/">
+<%-- <ww:param name="q" value="%{'${q}'}"/> --%>
+ <ww:param name="rowCount" value="%{'${rowCount}'}"/>
+ <ww:param name="groupId" value="%{'${groupId}'}"/>
+ <ww:param name="artifactId" value="%{'${artifactId}'}"/>
+ <ww:param name="version" value="%{'${version}'}"/>
+ <ww:param name="className" value="%{'${className}'}"/>
+ <ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
+ <ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
+ <ww:param name="fromResultsPage" value="true"/>
+ <ww:param name="currentPage" value="%{'${i}'}"/>
+ <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
+ <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
+ </ww:url>
+ </c:set>
+ <a href="${specificPageUrl}">${i + 1}</a>
+ </c:when>
+ <c:otherwise>
+ <b>${i + 1}</b>
+ </c:otherwise>
+ </c:choose>
+ </c:if>
</c:forEach>
-
- <c:choose>
+
+ <c:choose>
<c:when test="${currentPage == (totalPages - 1)}">
<img src="${imgNextPageDisabledUrl}"/>
</c:when>