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