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;
28 import org.apache.archiva.indexer.util.SearchUtil;
29 import org.apache.commons.collections.CollectionUtils;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
32 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
33 import org.apache.maven.archiva.database.ArchivaDAO;
34 import org.apache.maven.archiva.database.Constraint;
35 import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
36 import org.apache.maven.archiva.indexer.RepositoryIndexException;
37 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
38 import org.apache.maven.archiva.indexer.search.CrossRepositorySearch;
39 import org.apache.maven.archiva.indexer.search.SearchResultLimits;
40 import org.apache.maven.archiva.indexer.search.SearchResults;
41 import org.apache.maven.archiva.security.AccessDeniedException;
42 import org.apache.maven.archiva.security.ArchivaSecurityException;
43 import org.apache.maven.archiva.security.ArchivaXworkUser;
44 import org.apache.maven.archiva.security.PrincipalNotFoundException;
45 import org.apache.maven.archiva.security.UserRepositories;
47 import com.opensymphony.xwork2.ActionContext;
48 import com.opensymphony.xwork2.Preparable;
49 import org.apache.maven.archiva.database.constraints.UniqueVersionConstraint;
50 import org.apache.maven.archiva.indexer.search.SearchResultHit;
53 * Search all indexed fields by the given criteria.
55 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="searchAction"
57 public class SearchAction
58 extends PlexusActionSupport
65 private ArchivaConfiguration archivaConfiguration;
67 private Map<String, ManagedRepositoryConfiguration> managedRepositories;
72 * @plexus.requirement role-hint="jdo"
74 private ArchivaDAO dao;
79 private SearchResults results;
82 * @plexus.requirement role-hint="default"
84 private CrossRepositorySearch crossRepoSearch;
89 private UserRepositories userRepositories;
94 private ArchivaXworkUser archivaXworkUser;
96 private static final String RESULTS = "results";
98 private static final String ARTIFACT = "artifact";
100 private List databaseResults;
102 private int currentPage = 0;
104 private int totalPages;
106 private boolean searchResultsOnly;
108 private String completeQueryString;
110 private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
112 private List<String> managedRepositoryList;
114 private String groupId;
116 private String artifactId;
118 private String version;
120 private String className;
122 private int rowCount = 30;
124 private String repositoryId;
126 private boolean fromFilterSearch;
128 private boolean filterSearch = false;
130 private boolean fromResultsPage;
132 public boolean isFromResultsPage()
134 return fromResultsPage;
137 public void setFromResultsPage( boolean fromResultsPage )
139 this.fromResultsPage = fromResultsPage;
142 public boolean isFromFilterSearch()
144 return fromFilterSearch;
147 public void setFromFilterSearch( boolean fromFilterSearch )
149 this.fromFilterSearch = fromFilterSearch;
152 public void prepare()
154 managedRepositoryList = new ArrayList<String>();
155 managedRepositoryList = getObservableRepos();
157 if ( managedRepositoryList.size() > 0 )
159 managedRepositoryList.add( "all" );
163 // advanced search MRM-90 -- filtered search
164 public String filteredSearch()
165 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
167 fromFilterSearch = true;
169 if ( CollectionUtils.isEmpty( managedRepositoryList ) )
171 return GlobalResults.ACCESS_TO_NO_REPOS;
174 SearchResultLimits limits = new SearchResultLimits( currentPage );
176 limits.setPageSize( rowCount );
177 List<String> selectedRepos = new ArrayList<String>();
179 if ( repositoryId.equals( "all" ) )
181 selectedRepos = getObservableRepos();
185 selectedRepos.add( repositoryId );
188 if ( CollectionUtils.isEmpty( selectedRepos ) )
190 return GlobalResults.ACCESS_TO_NO_REPOS;
194 crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version,
197 if ( results.isEmpty() )
199 addActionError( "No results found" );
203 totalPages = results.getTotalHits() / limits.getPageSize();
205 if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
207 totalPages = totalPages + 1;
213 public String quickSearch()
214 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
216 /* TODO: give action message if indexing is in progress.
217 * This should be based off a count of 'unprocessed' artifacts.
218 * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
219 * present in the full text search.
222 assert q != null && q.length() != 0;
224 fromFilterSearch = false;
226 SearchResultLimits limits = new SearchResultLimits( currentPage );
228 List<String> selectedRepos = getObservableRepos();
229 if ( CollectionUtils.isEmpty( selectedRepos ) )
231 return GlobalResults.ACCESS_TO_NO_REPOS;
234 if( SearchUtil.isBytecodeSearch( q ) )
236 results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, SearchUtil.removeBytecodeKeyword( q ), limits );
240 if( searchResultsOnly && !completeQueryString.equals( "" ) )
242 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
246 completeQueryString = "";
247 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
251 if ( results.isEmpty() )
253 addActionError( "No results found" );
257 totalPages = results.getTotalHits() / limits.getPageSize();
259 if( (results.getTotalHits() % limits.getPageSize()) != 0 )
261 totalPages = totalPages + 1;
263 // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
265 /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
266 * the future to return relevant links better.
267 * I expect the lucene scoring system to take multiple hits on different areas of a single document
268 * to result in a higher score.
272 if( !isEqualToPreviousSearchTerm( q ) )
274 buildCompleteQueryString( q );
277 //Lets get the versions for the artifact we just found and display them
278 //Yes, this is in the lucene index but its more challenging to get them out when we are searching by project
279 for (SearchResultHit resultHit : results.getHits())
281 final List<String> versions = dao.query(new UniqueVersionConstraint(getObservableRepos(), resultHit.getGroupId(), resultHit.getArtifactId()));
282 if (versions != null && !versions.isEmpty())
284 resultHit.setVersion(null);
285 resultHit.setVersions(versions);
292 public String findArtifact()
295 // TODO: give action message if indexing is in progress
297 if ( StringUtils.isBlank( q ) )
299 addActionError( "Unable to search for a blank checksum" );
303 Constraint constraint = new ArtifactsByChecksumConstraint( q );
304 databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
306 if ( databaseResults.isEmpty() )
308 addActionError( "No results found" );
312 if ( databaseResults.size() == 1 )
314 // 1 hit? return it's information directly!
321 public String doInput()
326 private String getPrincipal()
328 return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
331 private List<String> getObservableRepos()
335 return userRepositories.getObservableRepositoryIds( getPrincipal() );
337 catch ( PrincipalNotFoundException e )
339 getLogger().warn( e.getMessage(), e );
341 catch ( AccessDeniedException e )
343 getLogger().warn( e.getMessage(), e );
345 catch ( ArchivaSecurityException e )
347 getLogger().warn( e.getMessage(), e );
349 return Collections.emptyList();
352 private void buildCompleteQueryString( String searchTerm )
354 if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
356 searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
359 if ( completeQueryString == null || "".equals( completeQueryString ) )
361 completeQueryString = searchTerm;
365 completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
369 private List<String> parseCompleteQueryString()
371 List<String> parsedCompleteQueryString = new ArrayList<String>();
372 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
373 CollectionUtils.addAll( parsedCompleteQueryString, parsed );
375 return parsedCompleteQueryString;
378 private boolean isEqualToPreviousSearchTerm( String searchTerm )
380 if ( !"".equals( completeQueryString ) )
382 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
383 if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
397 public void setQ( String q )
402 public SearchResults getResults()
407 public List getDatabaseResults()
409 return databaseResults;
412 public void setCurrentPage( int page )
414 this.currentPage = page;
417 public int getCurrentPage()
422 public int getTotalPages()
427 public void setTotalPages( int totalPages )
429 this.totalPages = totalPages;
432 public boolean isSearchResultsOnly()
434 return searchResultsOnly;
437 public void setSearchResultsOnly( boolean searchResultsOnly )
439 this.searchResultsOnly = searchResultsOnly;
442 public String getCompleteQueryString()
444 return completeQueryString;
447 public void setCompleteQueryString( String completeQueryString )
449 this.completeQueryString = completeQueryString;
452 public ArchivaConfiguration getArchivaConfiguration()
454 return archivaConfiguration;
457 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
459 this.archivaConfiguration = archivaConfiguration;
462 public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
464 return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
467 public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
469 this.managedRepositories = managedRepositories;
472 public String getGroupId()
477 public void setGroupId( String groupId )
479 this.groupId = groupId;
482 public String getArtifactId()
487 public void setArtifactId( String artifactId )
489 this.artifactId = artifactId;
492 public String getVersion()
497 public void setVersion( String version )
499 this.version = version;
502 public int getRowCount()
507 public void setRowCount( int rowCount )
509 this.rowCount = rowCount;
512 public boolean isFilterSearch()
517 public void setFilterSearch( boolean filterSearch )
519 this.filterSearch = filterSearch;
522 public String getRepositoryId()
527 public void setRepositoryId( String repositoryId )
529 this.repositoryId = repositoryId;
532 public List<String> getManagedRepositoryList()
534 return managedRepositoryList;
537 public void setManagedRepositoryList( List<String> managedRepositoryList )
539 this.managedRepositoryList = managedRepositoryList;
542 public String getClassName()
547 public void setClassName( String className )
549 this.className = className;