| 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 |
| * 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.net.MalformedURLException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| 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; |
| import org.apache.maven.archiva.indexer.RepositoryIndexException; |
| import org.apache.maven.archiva.indexer.RepositoryIndexSearchException; |
| import org.apache.maven.archiva.indexer.search.CrossRepositorySearch; |
| import org.apache.maven.archiva.indexer.search.SearchResultLimits; |
| import org.apache.maven.archiva.indexer.search.SearchResults; |
| import org.apache.maven.archiva.security.AccessDeniedException; |
| import org.apache.maven.archiva.security.ArchivaSecurityException; |
| import org.apache.maven.archiva.security.ArchivaXworkUser; |
| import org.apache.maven.archiva.security.PrincipalNotFoundException; |
| import org.apache.maven.archiva.security.UserRepositories; |
| |
| import com.opensymphony.xwork2.ActionContext; |
| import com.opensymphony.xwork2.Preparable; |
| |
| /** |
| * Search all indexed fields by the given criteria. |
| * |
| * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="searchAction" |
| */ |
| public class SearchAction |
| extends PlexusActionSupport |
| implements Preparable |
| { |
| /** |
| * Query string. |
| */ |
| |
| private ArchivaConfiguration archivaConfiguration; |
| |
| private Map<String, ManagedRepositoryConfiguration> managedRepositories; |
| |
| private String q; |
| |
| /** |
| * @plexus.requirement role-hint="jdo" |
| */ |
| private ArchivaDAO dao; |
| |
| /** |
| * The Search Results. |
| */ |
| private SearchResults results; |
| |
| /** |
| * @plexus.requirement role-hint="default" |
| */ |
| private CrossRepositorySearch crossRepoSearch; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private UserRepositories userRepositories; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private ArchivaXworkUser archivaXworkUser; |
| |
| private static final String RESULTS = "results"; |
| |
| private static final String ARTIFACT = "artifact"; |
| |
| private List databaseResults; |
| |
| private int currentPage = 0; |
| |
| private int totalPages; |
| |
| 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 |
| * 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 ) ) |
| { |
| return GlobalResults.ACCESS_TO_NO_REPOS; |
| } |
| |
| 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 |
| { |
| completeQueryString = ""; |
| 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 |
| * 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 |
| */ |
| |
| if( !isEqualToPreviousSearchTerm( q ) ) |
| { |
| buildCompleteQueryString( q ); |
| } |
| |
| return SUCCESS; |
| } |
| |
| public String findArtifact() |
| throws Exception |
| { |
| // TODO: give action message if indexing is in progress |
| |
| if ( StringUtils.isBlank( q ) ) |
| { |
| addActionError( "Unable to search for a blank checksum" ); |
| return INPUT; |
| } |
| |
| Constraint constraint = new ArtifactsByChecksumConstraint( q ); |
| databaseResults = dao.getArtifactDAO().queryArtifacts( constraint ); |
| |
| if ( databaseResults.isEmpty() ) |
| { |
| addActionError( "No results found" ); |
| return INPUT; |
| } |
| |
| if ( databaseResults.size() == 1 ) |
| { |
| // 1 hit? return it's information directly! |
| return ARTIFACT; |
| } |
| |
| return RESULTS; |
| } |
| |
| public String doInput() |
| { |
| return INPUT; |
| } |
| |
| private String getPrincipal() |
| { |
| return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() ); |
| } |
| |
| private List<String> getObservableRepos() |
| { |
| try |
| { |
| return userRepositories.getObservableRepositoryIds( getPrincipal() ); |
| } |
| catch ( PrincipalNotFoundException e ) |
| { |
| getLogger().warn( e.getMessage(), e ); |
| } |
| catch ( AccessDeniedException e ) |
| { |
| getLogger().warn( e.getMessage(), e ); |
| // TODO: pass this onto the screen. |
| } |
| catch ( ArchivaSecurityException e ) |
| { |
| getLogger().warn( e.getMessage(), e ); |
| } |
| return Collections.emptyList(); |
| } |
| |
| private void buildCompleteQueryString( String searchTerm ) |
| { |
| if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 ) |
| { |
| searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR ); |
| } |
| |
| 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>(); |
| String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); |
| CollectionUtils.addAll( parsedCompleteQueryString, parsed ); |
| |
| return parsedCompleteQueryString; |
| } |
| |
| private boolean isEqualToPreviousSearchTerm( String searchTerm ) |
| { |
| if ( !"".equals( completeQueryString ) ) |
| { |
| String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); |
| if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) ) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public String getQ() |
| { |
| return q; |
| } |
| |
| public void setQ( String q ) |
| { |
| this.q = q; |
| } |
| |
| public SearchResults getResults() |
| { |
| return results; |
| } |
| |
| public List getDatabaseResults() |
| { |
| return databaseResults; |
| } |
| |
| public void setCurrentPage( int page ) |
| { |
| this.currentPage = page; |
| } |
| |
| public int getCurrentPage() |
| { |
| return currentPage; |
| } |
| |
| public int getTotalPages() |
| { |
| return totalPages; |
| } |
| |
| public void setTotalPages( int totalPages ) |
| { |
| this.totalPages = totalPages; |
| } |
| |
| public boolean isSearchResultsOnly() |
| { |
| return searchResultsOnly; |
| } |
| |
| public void setSearchResultsOnly( boolean searchResultsOnly ) |
| { |
| this.searchResultsOnly = searchResultsOnly; |
| } |
| |
| public String getCompleteQueryString() |
| { |
| return completeQueryString; |
| } |
| |
| public void setCompleteQueryString( String completeQueryString ) |
| { |
| this.completeQueryString = completeQueryString; |
| } |
| |
| private boolean isBytecodeSearch( String queryString ) |
| { |
| if ( queryString.startsWith( BYTECODE_KEYWORD ) ) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private String removeKeyword( String queryString ) |
| { |
| String qString = StringUtils.uncapitalize( queryString ); |
| 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; |
| } |
| } |