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