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.commons.collections.CollectionUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
31 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
32 import org.apache.maven.archiva.database.ArchivaDAO;
33 import org.apache.maven.archiva.database.Constraint;
34 import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
35 import org.apache.maven.archiva.indexer.RepositoryIndexException;
36 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
37 import org.apache.maven.archiva.indexer.search.CrossRepositorySearch;
38 import org.apache.maven.archiva.indexer.search.SearchResultLimits;
39 import org.apache.maven.archiva.indexer.search.SearchResults;
40 import org.apache.maven.archiva.security.AccessDeniedException;
41 import org.apache.maven.archiva.security.ArchivaSecurityException;
42 import org.apache.maven.archiva.security.ArchivaXworkUser;
43 import org.apache.maven.archiva.security.PrincipalNotFoundException;
44 import org.apache.maven.archiva.security.UserRepositories;
45 import org.codehaus.plexus.xwork.action.PlexusActionSupport;
47 import com.opensymphony.xwork.ActionContext;
48 import com.opensymphony.xwork.Preparable;
51 * Search all indexed fields by the given criteria.
53 * @plexus.component role="com.opensymphony.xwork.Action" role-hint="searchAction"
55 public class SearchAction
56 extends PlexusActionSupport
63 private ArchivaConfiguration archivaConfiguration;
65 private Map<String, ManagedRepositoryConfiguration> managedRepositories;
70 * @plexus.requirement role-hint="jdo"
72 private ArchivaDAO dao;
77 private SearchResults results;
80 * @plexus.requirement role-hint="default"
82 private CrossRepositorySearch crossRepoSearch;
87 private UserRepositories userRepositories;
92 private ArchivaXworkUser archivaXworkUser;
94 private static final String RESULTS = "results";
96 private static final String ARTIFACT = "artifact";
98 private List databaseResults;
100 private int currentPage = 0;
102 private int totalPages;
104 private boolean searchResultsOnly;
106 private String completeQueryString;
108 private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
110 private static final String BYTECODE_KEYWORD = "bytecode:";
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;
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;
196 crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version,
199 if ( results.isEmpty() )
201 addActionError( "No results found" );
205 totalPages = results.getTotalHits() / limits.getPageSize();
207 if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
209 totalPages = totalPages + 1;
215 public String quickSearch()
216 throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
218 /* TODO: give action message if indexing is in progress.
219 * This should be based off a count of 'unprocessed' artifacts.
220 * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
221 * present in the full text search.
224 assert q != null && q.length() != 0;
226 fromFilterSearch = false;
228 SearchResultLimits limits = new SearchResultLimits( currentPage );
230 List<String> selectedRepos = getObservableRepos();
231 if ( CollectionUtils.isEmpty( selectedRepos ) )
233 return GlobalResults.ACCESS_TO_NO_REPOS;
236 if( isBytecodeSearch( q ) )
238 results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, removeKeyword( q ), limits );
242 if( searchResultsOnly && !completeQueryString.equals( "" ) )
244 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
248 completeQueryString = "";
249 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
253 if ( results.isEmpty() )
255 addActionError( "No results found" );
259 totalPages = results.getTotalHits() / limits.getPageSize();
261 if( (results.getTotalHits() % limits.getPageSize()) != 0 )
263 totalPages = totalPages + 1;
265 // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
267 /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
268 * the future to return relevant links better.
269 * I expect the lucene scoring system to take multiple hits on different areas of a single document
270 * to result in a higher score.
274 if( !isEqualToPreviousSearchTerm( q ) )
276 buildCompleteQueryString( q );
282 public String findArtifact()
285 // TODO: give action message if indexing is in progress
287 if ( StringUtils.isBlank( q ) )
289 addActionError( "Unable to search for a blank checksum" );
293 Constraint constraint = new ArtifactsByChecksumConstraint( q );
294 databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
296 if ( databaseResults.isEmpty() )
298 addActionError( "No results found" );
302 if ( databaseResults.size() == 1 )
304 // 1 hit? return it's information directly!
312 public String doInput()
317 private String getPrincipal()
319 return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
322 private List<String> getObservableRepos()
326 return userRepositories.getObservableRepositoryIds( getPrincipal() );
328 catch ( PrincipalNotFoundException e )
330 getLogger().warn( e.getMessage(), e );
332 catch ( AccessDeniedException e )
334 getLogger().warn( e.getMessage(), e );
335 // TODO: pass this onto the screen.
337 catch ( ArchivaSecurityException e )
339 getLogger().warn( e.getMessage(), e );
341 return Collections.emptyList();
344 private void buildCompleteQueryString( String searchTerm )
346 if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
348 searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
351 if ( completeQueryString == null || "".equals( completeQueryString ) )
353 completeQueryString = searchTerm;
357 completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
361 private List<String> parseCompleteQueryString()
363 List<String> parsedCompleteQueryString = new ArrayList<String>();
364 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
365 CollectionUtils.addAll( parsedCompleteQueryString, parsed );
367 return parsedCompleteQueryString;
370 private boolean isEqualToPreviousSearchTerm( String searchTerm )
372 if ( !"".equals( completeQueryString ) )
374 String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
375 if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
389 public void setQ( String q )
394 public SearchResults getResults()
399 public List getDatabaseResults()
401 return databaseResults;
404 public void setCurrentPage( int page )
406 this.currentPage = page;
409 public int getCurrentPage()
414 public int getTotalPages()
419 public void setTotalPages( int totalPages )
421 this.totalPages = totalPages;
424 public boolean isSearchResultsOnly()
426 return searchResultsOnly;
429 public void setSearchResultsOnly( boolean searchResultsOnly )
431 this.searchResultsOnly = searchResultsOnly;
434 public String getCompleteQueryString()
436 return completeQueryString;
439 public void setCompleteQueryString( String completeQueryString )
441 this.completeQueryString = completeQueryString;
444 private boolean isBytecodeSearch( String queryString )
446 if ( queryString.startsWith( BYTECODE_KEYWORD ) )
454 private String removeKeyword( String queryString )
456 String qString = StringUtils.uncapitalize( queryString );
457 qString = StringUtils.remove( queryString, BYTECODE_KEYWORD );
462 public ArchivaConfiguration getArchivaConfiguration()
464 return archivaConfiguration;
467 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
469 this.archivaConfiguration = archivaConfiguration;
472 public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
474 return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
477 public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
479 this.managedRepositories = managedRepositories;
482 public String getGroupId()
487 public void setGroupId( String groupId )
489 this.groupId = groupId;
492 public String getArtifactId()
497 public void setArtifactId( String artifactId )
499 this.artifactId = artifactId;
502 public String getVersion()
507 public void setVersion( String version )
509 this.version = version;
512 public int getRowCount()
517 public void setRowCount( int rowCount )
519 this.rowCount = rowCount;
522 public boolean isFilterSearch()
527 public void setFilterSearch( boolean filterSearch )
529 this.filterSearch = filterSearch;
532 public String getRepositoryId()
537 public void setRepositoryId( String repositoryId )
539 this.repositoryId = repositoryId;
542 public List<String> getManagedRepositoryList()
544 return managedRepositoryList;
547 public void setManagedRepositoryList( List<String> managedRepositoryList )
549 this.managedRepositoryList = managedRepositoryList;
552 public String getClassName()
557 public void setClassName( String className )
559 this.className = className;