1 package org.apache.maven.archiva.web.action;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import java.net.MalformedURLException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
27 import com.opensymphony.xwork.ActionContext;
28 import org.apache.commons.collections.CollectionUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.maven.archiva.database.ArchivaDAO;
31 import org.apache.maven.archiva.database.Constraint;
32 import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
33 import org.apache.maven.archiva.indexer.RepositoryIndexException;
34 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
35 import org.apache.maven.archiva.indexer.search.CrossRepositorySearch;
36 import org.apache.maven.archiva.indexer.search.SearchResultLimits;
37 import org.apache.maven.archiva.indexer.search.SearchResults;
38 import org.apache.maven.archiva.security.AccessDeniedException;
39 import org.apache.maven.archiva.security.ArchivaSecurityException;
40 import org.apache.maven.archiva.security.ArchivaXworkUser;
41 import org.apache.maven.archiva.security.PrincipalNotFoundException;
42 import org.apache.maven.archiva.security.UserRepositories;
43 import org.codehaus.plexus.xwork.action.PlexusActionSupport;
46 * Search all indexed fields by the given criteria.
48 * @plexus.component role="com.opensymphony.xwork.Action" role-hint="searchAction"
50 public class SearchAction
51 extends PlexusActionSupport
59 * @plexus.requirement role-hint="jdo"
61 private ArchivaDAO dao;
66 private SearchResults results;
69 * @plexus.requirement role-hint="default"
71 private CrossRepositorySearch crossRepoSearch;
76 private UserRepositories userRepositories;
78 private static final String RESULTS = "results";
80 private static final String ARTIFACT = "artifact";
82 private List databaseResults;
84 private int currentPage = 0;
86 private int totalPages;
88 private boolean searchResultsOnly;
90 private String completeQueryString;
92 private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
94 public String quickSearch()
95 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
97 /* TODO: give action message if indexing is in progress.
98 * This should be based off a count of 'unprocessed' artifacts.
99 * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
100 * present in the full text search.
103 assert q != null && q.length() != 0;
105 SearchResultLimits limits = new SearchResultLimits( currentPage );
107 List<String> selectedRepos = getObservableRepos();
108 if ( CollectionUtils.isEmpty( selectedRepos ) )
110 return GlobalResults.ACCESS_TO_NO_REPOS;
113 if( searchResultsOnly && !completeQueryString.equals( "" ) )
115 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
119 completeQueryString = "";
120 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
123 if ( results.isEmpty() )
125 addActionError( "No results found" );
129 totalPages = results.getTotalHits() / limits.getPageSize();
131 if( (results.getTotalHits() % limits.getPageSize()) != 0 )
133 totalPages = totalPages + 1;
135 // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
137 /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
138 * the future to return relevant links better.
139 * I expect the lucene scoring system to take multiple hits on different areas of a single document
140 * to result in a higher score.
144 if( !isEqualToPreviousSearchTerm( q ) )
146 buildCompleteQueryString( q );
152 public String findArtifact()
155 // TODO: give action message if indexing is in progress
157 if ( StringUtils.isBlank( q ) )
159 addActionError( "Unable to search for a blank checksum" );
163 Constraint constraint = new ArtifactsByChecksumConstraint( q );
164 databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
166 if ( databaseResults.isEmpty() )
168 addActionError( "No results found" );
172 if ( databaseResults.size() == 1 )
174 // 1 hit? return it's information directly!
182 public String doInput()
187 private String getPrincipal()
189 return ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
192 private List<String> getObservableRepos()
196 return userRepositories.getObservableRepositoryIds( getPrincipal() );
198 catch ( PrincipalNotFoundException e )
200 getLogger().warn( e.getMessage(), e );
202 catch ( AccessDeniedException e )
204 getLogger().warn( e.getMessage(), e );
205 // TODO: pass this onto the screen.
207 catch ( ArchivaSecurityException e )
209 getLogger().warn( e.getMessage(), e );
211 return Collections.emptyList();
214 private void buildCompleteQueryString( String searchTerm )
216 if( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
218 searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
221 if( completeQueryString == null || "".equals( completeQueryString ) )
223 completeQueryString = searchTerm;
227 completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
231 private List<String> parseCompleteQueryString()
233 List<String> parsedCompleteQueryString = new ArrayList<String>();
234 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
235 CollectionUtils.addAll( parsedCompleteQueryString, parsed );
237 return parsedCompleteQueryString;
240 private boolean isEqualToPreviousSearchTerm( String searchTerm )
242 if( !"".equals( completeQueryString ) )
244 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
245 if( StringUtils.equalsIgnoreCase( searchTerm, parsed[ parsed.length - 1 ] ) )
259 public void setQ( String q )
264 public SearchResults getResults()
269 public List getDatabaseResults()
271 return databaseResults;
274 public void setCurrentPage( int page )
276 this.currentPage = page;
279 public int getCurrentPage()
284 public int getTotalPages()
289 public void setTotalPages( int totalPages )
291 this.totalPages = totalPages;
294 public boolean isSearchResultsOnly()
296 return searchResultsOnly;
299 public void setSearchResultsOnly( boolean searchResultsOnly )
301 this.searchResultsOnly = searchResultsOnly;
304 public String getCompleteQueryString()
306 return completeQueryString;
309 public void setCompleteQueryString( String completeQueryString )
311 this.completeQueryString = completeQueryString;