]> source.dussan.org Git - archiva.git/blob
35e770b26a3640906aca51cb4cf6bdee839fefca
[archiva.git] /
1 package org.apache.maven.archiva.web.action;
2         
3 /*
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
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
22 import java.net.MalformedURLException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27
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;
46
47 import com.opensymphony.xwork.ActionContext;
48 import com.opensymphony.xwork.Preparable;
49
50 /**
51  * Search all indexed fields by the given criteria.
52  *
53  * @plexus.component role="com.opensymphony.xwork.Action" role-hint="searchAction"
54  */
55 public class SearchAction
56     extends PlexusActionSupport
57     implements Preparable
58 {
59     /**
60      * Query string.
61      */
62
63     private ArchivaConfiguration archivaConfiguration;
64
65     private Map<String, ManagedRepositoryConfiguration> managedRepositories;
66
67     private String q;
68
69     /**
70      * @plexus.requirement role-hint="jdo"
71      */
72     private ArchivaDAO dao;
73
74     /**
75      * The Search Results.
76      */
77     private SearchResults results;
78
79     /**
80      * @plexus.requirement role-hint="default"
81      */
82     private CrossRepositorySearch crossRepoSearch;
83     
84     /**
85      * @plexus.requirement
86      */
87     private UserRepositories userRepositories;
88     
89     /**
90      * @plexus.requirement
91      */
92     private ArchivaXworkUser archivaXworkUser;
93     
94     private static final String RESULTS = "results";
95
96     private static final String ARTIFACT = "artifact";
97
98     private List databaseResults;
99     
100     private int currentPage = 0;
101     
102     private int totalPages;
103     
104     private boolean searchResultsOnly;
105     
106     private String completeQueryString;
107     
108     private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
109
110     private static final String BYTECODE_KEYWORD = "bytecode:";
111
112     private List<String> managedRepositoryList;
113
114     private String groupId;
115
116     private String artifactId;
117
118     private String version;
119
120     private String className;
121
122     private int rowCount = 30;
123
124     private String repositoryId;
125
126     private boolean fromFilterSearch;
127
128     private boolean filterSearch = false;
129
130     private boolean fromResultsPage;
131
132     private int num;
133
134     public boolean isFromResultsPage()
135     {
136         return fromResultsPage;
137     }
138
139     public void setFromResultsPage( boolean fromResultsPage )
140     {
141         this.fromResultsPage = fromResultsPage;
142     }
143
144     public boolean isFromFilterSearch()
145     {
146         return fromFilterSearch;
147     }
148
149     public void setFromFilterSearch( boolean fromFilterSearch )
150     {
151         this.fromFilterSearch = fromFilterSearch;
152     }
153
154     public void prepare()
155     {
156         managedRepositoryList = new ArrayList<String>();
157         managedRepositoryList = getObservableRepos();
158
159         if ( managedRepositoryList.size() > 0 )
160         {
161             managedRepositoryList.add( "all" );
162         }
163     }
164
165     // advanced search MRM-90 -- filtered search
166     public String filteredSearch()
167         throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
168     {
169         fromFilterSearch = true;
170
171         if ( CollectionUtils.isEmpty( managedRepositoryList ) )
172         {
173             return GlobalResults.ACCESS_TO_NO_REPOS;
174         }
175
176         SearchResultLimits limits = new SearchResultLimits( currentPage );
177
178         limits.setPageSize( rowCount );
179         List<String> selectedRepos = new ArrayList<String>();
180
181         if ( repositoryId.equals( "all" ) )
182         {
183             selectedRepos = getObservableRepos();
184         }
185         else
186         {
187             selectedRepos.add( repositoryId );
188         }
189
190         if ( CollectionUtils.isEmpty( selectedRepos ) )
191         {
192             return GlobalResults.ACCESS_TO_NO_REPOS;
193         }
194
195         results =
196             crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version,
197                                                    className, limits );
198
199         if ( results.isEmpty() )
200         {
201             addActionError( "No results found" );
202             return INPUT;
203         }
204
205         totalPages = results.getTotalHits() / limits.getPageSize();
206
207         if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
208         {
209             totalPages = totalPages + 1;
210         }
211
212         return SUCCESS;
213     }
214
215     public String quickSearch()
216         throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
217     {
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.
222          */
223
224         assert q != null && q.length() != 0;
225
226         fromFilterSearch = false;
227
228         SearchResultLimits limits = new SearchResultLimits( currentPage );
229
230         List<String> selectedRepos = getObservableRepos();
231         if ( CollectionUtils.isEmpty( selectedRepos ) )
232         {
233             return GlobalResults.ACCESS_TO_NO_REPOS;
234         }
235
236         if( isBytecodeSearch( q ) )
237         {
238             results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, removeKeyword( q ), limits );
239         }
240         else
241         {
242             if( searchResultsOnly && !completeQueryString.equals( "" ) )
243             {
244                 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
245             }
246             else
247             {
248                 completeQueryString = "";
249                 results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
250             }
251         }
252
253         if ( results.isEmpty() )
254         {
255             addActionError( "No results found" );
256             return INPUT;
257         }
258
259         totalPages = results.getTotalHits() / limits.getPageSize();
260
261         if( (results.getTotalHits() % limits.getPageSize()) != 0 )
262         {
263             totalPages = totalPages + 1;
264         }
265         // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
266
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.
271          *   - Joakim
272          */
273
274         if( !isEqualToPreviousSearchTerm( q ) )
275         {
276             buildCompleteQueryString( q );
277         }
278
279         return SUCCESS;
280     }
281
282     public String findArtifact()
283         throws Exception
284     {
285         // TODO: give action message if indexing is in progress
286
287         if ( StringUtils.isBlank( q ) )
288         {
289             addActionError( "Unable to search for a blank checksum" );
290             return INPUT;
291         }
292
293         Constraint constraint = new ArtifactsByChecksumConstraint( q );
294         databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
295
296         if ( databaseResults.isEmpty() )
297         {
298             addActionError( "No results found" );
299             return INPUT;
300         }
301
302         if ( databaseResults.size() == 1 )
303         {
304             // 1 hit? return it's information directly!
305             return ARTIFACT;
306         }
307
308         return RESULTS;
309     }
310
311     @Override
312     public String doInput()
313     {
314         return INPUT;
315     }
316
317     private String getPrincipal()
318     {
319         return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
320     }
321
322     private List<String> getObservableRepos()
323     {
324         try
325         {
326             return userRepositories.getObservableRepositoryIds( getPrincipal() );
327         }
328         catch ( PrincipalNotFoundException e )
329         {
330             getLogger().warn( e.getMessage(), e );
331         }
332         catch ( AccessDeniedException e )
333         {
334             getLogger().warn( e.getMessage(), e );
335             // TODO: pass this onto the screen.
336         }
337         catch ( ArchivaSecurityException e )
338         {
339             getLogger().warn( e.getMessage(), e );
340         }
341         return Collections.emptyList();
342     }
343
344     private void buildCompleteQueryString( String searchTerm )
345     {
346         if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
347         {
348             searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
349         }
350
351         if ( completeQueryString == null || "".equals( completeQueryString ) )
352         {
353             completeQueryString = searchTerm;
354         }
355         else
356         {
357             completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
358         }
359     }
360
361     private List<String> parseCompleteQueryString()
362     {
363         List<String> parsedCompleteQueryString = new ArrayList<String>();
364         String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
365         CollectionUtils.addAll( parsedCompleteQueryString, parsed );
366
367         return parsedCompleteQueryString;
368     }
369
370     private boolean isEqualToPreviousSearchTerm( String searchTerm )
371     {
372         if ( !"".equals( completeQueryString ) )
373         {
374             String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
375             if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
376             {
377                 return true;
378             }
379         }
380
381         return false;
382     }
383
384     public String getQ()
385     {
386         return q;
387     }
388
389     public void setQ( String q )
390     {
391         this.q = q;
392     }
393
394     public SearchResults getResults()
395     {
396         return results;
397     }
398
399     public List getDatabaseResults()
400     {
401         return databaseResults;
402     }
403
404     public void setCurrentPage( int page )
405     {
406         this.currentPage = page;
407     }
408
409     public int getCurrentPage()
410     {
411         return currentPage;
412     }
413
414     public int getTotalPages()
415     {
416         return totalPages;
417     }
418
419     public void setTotalPages( int totalPages )
420     {
421         this.totalPages = totalPages;
422     }
423
424     public boolean isSearchResultsOnly()
425     {
426         return searchResultsOnly;
427     }
428
429     public void setSearchResultsOnly( boolean searchResultsOnly )
430     {
431         this.searchResultsOnly = searchResultsOnly;
432     }
433
434     public String getCompleteQueryString()
435     {
436         return completeQueryString;
437     }
438
439     public void setCompleteQueryString( String completeQueryString )
440     {
441         this.completeQueryString = completeQueryString;
442     }
443
444     private boolean isBytecodeSearch( String queryString )
445     {
446         if ( queryString.startsWith( BYTECODE_KEYWORD ) )
447         {
448             return true;
449         }
450
451         return false;
452     }
453
454     private String removeKeyword( String queryString )
455     {
456         String qString = StringUtils.uncapitalize( queryString );
457         qString = StringUtils.remove( queryString, BYTECODE_KEYWORD );
458
459         return qString;
460     }
461
462     public ArchivaConfiguration getArchivaConfiguration()
463     {
464         return archivaConfiguration;
465     }
466
467     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
468     {
469         this.archivaConfiguration = archivaConfiguration;
470     }
471
472     public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
473     {
474         return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
475     }
476
477     public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
478     {
479         this.managedRepositories = managedRepositories;
480     }
481
482     public String getGroupId()
483     {
484         return groupId;
485     }
486
487     public void setGroupId( String groupId )
488     {
489         this.groupId = groupId;
490     }
491
492     public String getArtifactId()
493     {
494         return artifactId;
495     }
496
497     public void setArtifactId( String artifactId )
498     {
499         this.artifactId = artifactId;
500     }
501
502     public String getVersion()
503     {
504         return version;
505     }
506
507     public void setVersion( String version )
508     {
509         this.version = version;
510     }
511
512     public int getRowCount()
513     {
514         return rowCount;
515     }
516
517     public void setRowCount( int rowCount )
518     {
519         this.rowCount = rowCount;
520     }
521
522     public boolean isFilterSearch()
523     {
524         return filterSearch;
525     }
526
527     public void setFilterSearch( boolean filterSearch )
528     {
529         this.filterSearch = filterSearch;
530     }
531
532     public String getRepositoryId()
533     {
534         return repositoryId;
535     }
536
537     public void setRepositoryId( String repositoryId )
538     {
539         this.repositoryId = repositoryId;
540     }
541
542     public List<String> getManagedRepositoryList()
543     {
544         return managedRepositoryList;
545     }
546
547     public void setManagedRepositoryList( List<String> managedRepositoryList )
548     {
549         this.managedRepositoryList = managedRepositoryList;
550     }
551
552     public String getClassName()
553     {
554         return className;
555     }
556
557     public void setClassName( String className )
558     {
559         this.className = className;
560     }
561 }