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.search.RepositorySearch;
29 import org.apache.archiva.indexer.search.RepositorySearchException;
30 import org.apache.archiva.indexer.search.SearchFields;
31 import org.apache.commons.collections.CollectionUtils;
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
34 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
35 import org.apache.maven.archiva.database.ArchivaDAO;
36 import org.apache.maven.archiva.database.Constraint;
37 import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
38 import org.apache.maven.archiva.indexer.RepositoryIndexException;
39 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
40 import org.apache.maven.archiva.indexer.search.SearchResultLimits;
41 import org.apache.maven.archiva.indexer.search.SearchResults;
42 import org.apache.maven.archiva.security.AccessDeniedException;
43 import org.apache.maven.archiva.security.ArchivaSecurityException;
44 import org.apache.maven.archiva.security.ArchivaXworkUser;
45 import org.apache.maven.archiva.security.PrincipalNotFoundException;
46 import org.apache.maven.archiva.security.UserRepositories;
48 import com.opensymphony.xwork2.ActionContext;
49 import com.opensymphony.xwork2.Preparable;
50 import org.apache.maven.archiva.common.utils.VersionUtil;
51 import org.apache.maven.archiva.database.constraints.UniqueVersionConstraint;
52 import org.apache.maven.archiva.indexer.search.SearchResultHit;
53 import org.apache.struts2.ServletActionContext;
54 import org.springframework.web.context.WebApplicationContext;
55 import org.springframework.web.context.support.WebApplicationContextUtils;
58 * Search all indexed fields by the given criteria.
60 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="searchAction"
62 public class SearchAction
63 extends PlexusActionSupport
70 private ArchivaConfiguration archivaConfiguration;
72 private Map<String, ManagedRepositoryConfiguration> managedRepositories;
77 * @plexus.requirement role-hint="jdo"
79 private ArchivaDAO dao;
84 private SearchResults results;
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 private RepositorySearch nexusSearch;
134 public boolean isFromResultsPage()
136 return fromResultsPage;
139 public void setFromResultsPage( boolean fromResultsPage )
141 this.fromResultsPage = fromResultsPage;
144 public boolean isFromFilterSearch()
146 return fromFilterSearch;
149 public void setFromFilterSearch( boolean fromFilterSearch )
151 this.fromFilterSearch = fromFilterSearch;
154 public void prepare()
156 managedRepositoryList = new ArrayList<String>();
157 managedRepositoryList = getObservableRepos();
159 if ( managedRepositoryList.size() > 0 )
161 managedRepositoryList.add( "all" );
165 // advanced search MRM-90 -- filtered search
166 public String filteredSearch()
167 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
169 fromFilterSearch = true;
171 if ( CollectionUtils.isEmpty( managedRepositoryList ) )
173 return GlobalResults.ACCESS_TO_NO_REPOS;
176 SearchResultLimits limits = new SearchResultLimits( currentPage );
178 limits.setPageSize( rowCount );
179 List<String> selectedRepos = new ArrayList<String>();
181 if ( repositoryId.equals( "all" ) )
183 selectedRepos = getObservableRepos();
187 selectedRepos.add( repositoryId );
190 if ( CollectionUtils.isEmpty( selectedRepos ) )
192 return GlobalResults.ACCESS_TO_NO_REPOS;
195 SearchFields searchFields = new SearchFields( groupId, artifactId, version, null, className, selectedRepos );
198 // TODO: add packaging in the list of fields for advanced search (UI)
201 results = getNexusSearch().search( getPrincipal(), searchFields, limits );
203 catch ( RepositorySearchException e )
205 addActionError( e.getMessage() );
209 if ( results.isEmpty() )
211 addActionError( "No results found" );
215 totalPages = results.getTotalHits() / limits.getPageSize();
217 if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
219 totalPages = totalPages + 1;
222 for (SearchResultHit hit : results.getHits())
224 final String version = hit.getVersion();
227 hit.setVersion(VersionUtil.getBaseVersion(version));
234 public String quickSearch()
235 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
237 /* TODO: give action message if indexing is in progress.
238 * This should be based off a count of 'unprocessed' artifacts.
239 * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
240 * present in the full text search.
243 assert q != null && q.length() != 0;
245 fromFilterSearch = false;
247 SearchResultLimits limits = new SearchResultLimits( currentPage );
249 List<String> selectedRepos = getObservableRepos();
250 if ( CollectionUtils.isEmpty( selectedRepos ) )
252 return GlobalResults.ACCESS_TO_NO_REPOS;
257 if( searchResultsOnly && !completeQueryString.equals( "" ) )
259 results = getNexusSearch().search( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
263 completeQueryString = "";
264 results = getNexusSearch().search( getPrincipal(), selectedRepos, q, limits, null );
267 catch ( RepositorySearchException e )
269 addActionError( e.getMessage() );
273 if ( results.isEmpty() )
275 addActionError( "No results found" );
279 totalPages = results.getTotalHits() / limits.getPageSize();
281 if( (results.getTotalHits() % limits.getPageSize()) != 0 )
283 totalPages = totalPages + 1;
285 // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
287 /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
288 * the future to return relevant links better.
289 * I expect the lucene scoring system to take multiple hits on different areas of a single document
290 * to result in a higher score.
294 if( !isEqualToPreviousSearchTerm( q ) )
296 buildCompleteQueryString( q );
299 //Lets get the versions for the artifact we just found and display them
300 //Yes, this is in the lucene index but its more challenging to get them out when we are searching by project
301 for (SearchResultHit resultHit : results.getHits())
303 final List<String> versions = dao.query(new UniqueVersionConstraint(getObservableRepos(), resultHit.getGroupId(), resultHit.getArtifactId()));
304 if (versions != null && !versions.isEmpty())
306 resultHit.setVersion(null);
307 resultHit.setVersions(filterTimestampedSnapshots(versions));
315 * Remove timestamped snapshots from versions
317 private static List<String> filterTimestampedSnapshots(List<String> versions)
319 final List<String> filtered = new ArrayList<String>();
320 for (final String version : versions)
322 final String baseVersion = VersionUtil.getBaseVersion(version);
323 if (!filtered.contains(baseVersion))
325 filtered.add(baseVersion);
331 public String findArtifact()
334 // TODO: give action message if indexing is in progress
336 if ( StringUtils.isBlank( q ) )
338 addActionError( "Unable to search for a blank checksum" );
342 Constraint constraint = new ArtifactsByChecksumConstraint( q );
343 databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
345 if ( databaseResults.isEmpty() )
347 addActionError( "No results found" );
351 if ( databaseResults.size() == 1 )
353 // 1 hit? return it's information directly!
360 public String doInput()
365 private String getPrincipal()
367 return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
370 private List<String> getObservableRepos()
374 return userRepositories.getObservableRepositoryIds( getPrincipal() );
376 catch ( PrincipalNotFoundException e )
378 getLogger().warn( e.getMessage(), e );
380 catch ( AccessDeniedException e )
382 getLogger().warn( e.getMessage(), e );
384 catch ( ArchivaSecurityException e )
386 getLogger().warn( e.getMessage(), e );
388 return Collections.emptyList();
391 private void buildCompleteQueryString( String searchTerm )
393 if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
395 searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
398 if ( completeQueryString == null || "".equals( completeQueryString ) )
400 completeQueryString = searchTerm;
404 completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
408 private List<String> parseCompleteQueryString()
410 List<String> parsedCompleteQueryString = new ArrayList<String>();
411 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
412 CollectionUtils.addAll( parsedCompleteQueryString, parsed );
414 return parsedCompleteQueryString;
417 private boolean isEqualToPreviousSearchTerm( String searchTerm )
419 if ( !"".equals( completeQueryString ) )
421 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
422 if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
436 public void setQ( String q )
441 public SearchResults getResults()
446 public List getDatabaseResults()
448 return databaseResults;
451 public void setCurrentPage( int page )
453 this.currentPage = page;
456 public int getCurrentPage()
461 public int getTotalPages()
466 public void setTotalPages( int totalPages )
468 this.totalPages = totalPages;
471 public boolean isSearchResultsOnly()
473 return searchResultsOnly;
476 public void setSearchResultsOnly( boolean searchResultsOnly )
478 this.searchResultsOnly = searchResultsOnly;
481 public String getCompleteQueryString()
483 return completeQueryString;
486 public void setCompleteQueryString( String completeQueryString )
488 this.completeQueryString = completeQueryString;
491 public ArchivaConfiguration getArchivaConfiguration()
493 return archivaConfiguration;
496 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
498 this.archivaConfiguration = archivaConfiguration;
501 public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
503 return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
506 public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
508 this.managedRepositories = managedRepositories;
511 public String getGroupId()
516 public void setGroupId( String groupId )
518 this.groupId = groupId;
521 public String getArtifactId()
526 public void setArtifactId( String artifactId )
528 this.artifactId = artifactId;
531 public String getVersion()
536 public void setVersion( String version )
538 this.version = version;
541 public int getRowCount()
546 public void setRowCount( int rowCount )
548 this.rowCount = rowCount;
551 public boolean isFilterSearch()
556 public void setFilterSearch( boolean filterSearch )
558 this.filterSearch = filterSearch;
561 public String getRepositoryId()
566 public void setRepositoryId( String repositoryId )
568 this.repositoryId = repositoryId;
571 public List<String> getManagedRepositoryList()
573 return managedRepositoryList;
576 public void setManagedRepositoryList( List<String> managedRepositoryList )
578 this.managedRepositoryList = managedRepositoryList;
581 public String getClassName()
586 public void setClassName( String className )
588 this.className = className;
591 public RepositorySearch getNexusSearch()
593 if( nexusSearch == null )
595 WebApplicationContext wac =
596 WebApplicationContextUtils.getRequiredWebApplicationContext( ServletActionContext.getServletContext() );
597 nexusSearch = ( RepositorySearch ) wac.getBean( "nexusSearch" );
602 public void setNexusSearch( RepositorySearch nexusSearch )
604 this.nexusSearch = nexusSearch;