]> source.dussan.org Git - archiva.git/blob
8d591191544430201bbcb24b088d8bc49d94dd09
[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.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;
47
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;
56
57 /**
58  * Search all indexed fields by the given criteria.
59  *
60  * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="searchAction"
61  */
62 public class SearchAction
63     extends PlexusActionSupport
64     implements Preparable
65 {
66     /**
67      * Query string.
68      */
69
70     private ArchivaConfiguration archivaConfiguration;
71
72     private Map<String, ManagedRepositoryConfiguration> managedRepositories;
73
74     private String q;
75
76     /**
77      * @plexus.requirement role-hint="jdo"
78      */
79     private ArchivaDAO dao;
80
81     /**
82      * The Search Results.
83      */
84     private SearchResults results;
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     private RepositorySearch nexusSearch;
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         SearchFields searchFields = new SearchFields( groupId, artifactId, version, null, className, selectedRepos );
196         
197         
198         // TODO: add packaging in the list of fields for advanced search (UI)
199         try
200         {
201             results = getNexusSearch().search( getPrincipal(), searchFields, limits );
202         }
203         catch ( RepositorySearchException e )
204         {
205             addActionError( e.getMessage() );
206             return ERROR;
207         }
208         
209         if ( results.isEmpty() )
210         {
211             addActionError( "No results found" );
212             return INPUT;
213         }
214
215         totalPages = results.getTotalHits() / limits.getPageSize();
216
217         if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
218         {
219             totalPages = totalPages + 1;
220         }
221
222         for (SearchResultHit hit : results.getHits())
223         {
224             final String version = hit.getVersion();
225             if (version != null)
226             {
227                 hit.setVersion(VersionUtil.getBaseVersion(version));
228             }
229         }
230
231         return SUCCESS;
232     }
233
234     public String quickSearch()
235         throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
236     {
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.
241          */
242
243         assert q != null && q.length() != 0;
244
245         fromFilterSearch = false;
246
247         SearchResultLimits limits = new SearchResultLimits( currentPage );
248
249         List<String> selectedRepos = getObservableRepos();
250         if ( CollectionUtils.isEmpty( selectedRepos ) )
251         {
252             return GlobalResults.ACCESS_TO_NO_REPOS;
253         }
254
255         try
256         {
257             if( searchResultsOnly && !completeQueryString.equals( "" ) )
258             {                       
259                 results = getNexusSearch().search( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );                   
260             }
261             else
262             {
263                 completeQueryString = "";                    
264                 results = getNexusSearch().search( getPrincipal(), selectedRepos, q, limits, null );                    
265             }
266         }
267         catch ( RepositorySearchException e )
268         {
269             addActionError( e.getMessage() );
270             return ERROR;
271         }
272
273         if ( results.isEmpty() )
274         {
275             addActionError( "No results found" );
276             return INPUT;
277         }
278
279         totalPages = results.getTotalHits() / limits.getPageSize();
280
281         if( (results.getTotalHits() % limits.getPageSize()) != 0 )
282         {
283             totalPages = totalPages + 1;
284         }
285         // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
286
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.
291          *   - Joakim
292          */
293
294         if( !isEqualToPreviousSearchTerm( q ) )
295         {
296             buildCompleteQueryString( q );
297         }
298        
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())
302         {
303             final List<String> versions = dao.query(new UniqueVersionConstraint(getObservableRepos(), resultHit.getGroupId(), resultHit.getArtifactId()));
304             if (versions != null && !versions.isEmpty())
305             {
306                 resultHit.setVersion(null);
307                 resultHit.setVersions(filterTimestampedSnapshots(versions));
308             }
309         }
310        
311         return SUCCESS;
312     }
313
314     /**
315      * Remove timestamped snapshots from versions
316      */
317     private static List<String> filterTimestampedSnapshots(List<String> versions)
318     {
319         final List<String> filtered = new ArrayList<String>();
320         for (final String version : versions)
321         {
322             final String baseVersion = VersionUtil.getBaseVersion(version);
323             if (!filtered.contains(baseVersion))
324             {
325                 filtered.add(baseVersion);
326             }
327         }
328         return filtered;
329     }
330
331     public String findArtifact()
332         throws Exception
333     {
334         // TODO: give action message if indexing is in progress
335
336         if ( StringUtils.isBlank( q ) )
337         {
338             addActionError( "Unable to search for a blank checksum" );
339             return INPUT;
340         }
341
342         Constraint constraint = new ArtifactsByChecksumConstraint( q );
343         databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
344
345         if ( databaseResults.isEmpty() )
346         {
347             addActionError( "No results found" );
348             return INPUT;
349         }
350
351         if ( databaseResults.size() == 1 )
352         {
353             // 1 hit? return it's information directly!
354             return ARTIFACT;
355         }
356
357         return RESULTS;
358     }
359     
360     public String doInput()
361     {
362         return INPUT;
363     }
364
365     private String getPrincipal()
366     {
367         return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
368     }
369
370     private List<String> getObservableRepos()
371     {
372         try
373         {
374             return userRepositories.getObservableRepositoryIds( getPrincipal() );
375         }
376         catch ( PrincipalNotFoundException e )
377         {
378             getLogger().warn( e.getMessage(), e );
379         }
380         catch ( AccessDeniedException e )
381         {
382             getLogger().warn( e.getMessage(), e );
383         }
384         catch ( ArchivaSecurityException e )
385         {
386             getLogger().warn( e.getMessage(), e );
387         }
388         return Collections.emptyList();
389     }
390
391     private void buildCompleteQueryString( String searchTerm )
392     {
393         if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
394         {
395             searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
396         }
397
398         if ( completeQueryString == null || "".equals( completeQueryString ) )
399         {
400             completeQueryString = searchTerm;
401         }
402         else
403         {
404             completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
405         }
406     }
407
408     private List<String> parseCompleteQueryString()
409     {
410         List<String> parsedCompleteQueryString = new ArrayList<String>();
411         String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
412         CollectionUtils.addAll( parsedCompleteQueryString, parsed );
413
414         return parsedCompleteQueryString;
415     }
416
417     private boolean isEqualToPreviousSearchTerm( String searchTerm )
418     {
419         if ( !"".equals( completeQueryString ) )
420         {
421             String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
422             if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
423             {
424                 return true;
425             }
426         }
427
428         return false;
429     }
430
431     public String getQ()
432     {
433         return q;
434     }
435
436     public void setQ( String q )
437     {
438         this.q = q;
439     }
440
441     public SearchResults getResults()
442     {
443         return results;
444     }
445
446     public List getDatabaseResults()
447     {
448         return databaseResults;
449     }
450
451     public void setCurrentPage( int page )
452     {
453         this.currentPage = page;
454     }
455
456     public int getCurrentPage()
457     {
458         return currentPage;
459     }
460
461     public int getTotalPages()
462     {
463         return totalPages;
464     }
465
466     public void setTotalPages( int totalPages )
467     {
468         this.totalPages = totalPages;
469     }
470
471     public boolean isSearchResultsOnly()
472     {
473         return searchResultsOnly;
474     }
475
476     public void setSearchResultsOnly( boolean searchResultsOnly )
477     {
478         this.searchResultsOnly = searchResultsOnly;
479     }
480
481     public String getCompleteQueryString()
482     {
483         return completeQueryString;
484     }
485
486     public void setCompleteQueryString( String completeQueryString )
487     {
488         this.completeQueryString = completeQueryString;
489     }
490
491     public ArchivaConfiguration getArchivaConfiguration()
492     {
493         return archivaConfiguration;
494     }
495
496     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
497     {
498         this.archivaConfiguration = archivaConfiguration;
499     }
500
501     public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
502     {
503         return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
504     }
505
506     public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
507     {
508         this.managedRepositories = managedRepositories;
509     }
510
511     public String getGroupId()
512     {
513         return groupId;
514     }
515
516     public void setGroupId( String groupId )
517     {
518         this.groupId = groupId;
519     }
520
521     public String getArtifactId()
522     {
523         return artifactId;
524     }
525
526     public void setArtifactId( String artifactId )
527     {
528         this.artifactId = artifactId;
529     }
530
531     public String getVersion()
532     {
533         return version;
534     }
535
536     public void setVersion( String version )
537     {
538         this.version = version;
539     }
540
541     public int getRowCount()
542     {
543         return rowCount;
544     }
545
546     public void setRowCount( int rowCount )
547     {
548         this.rowCount = rowCount;
549     }
550
551     public boolean isFilterSearch()
552     {
553         return filterSearch;
554     }
555
556     public void setFilterSearch( boolean filterSearch )
557     {
558         this.filterSearch = filterSearch;
559     }
560
561     public String getRepositoryId()
562     {
563         return repositoryId;
564     }
565
566     public void setRepositoryId( String repositoryId )
567     {
568         this.repositoryId = repositoryId;
569     }
570
571     public List<String> getManagedRepositoryList()
572     {
573         return managedRepositoryList;
574     }
575
576     public void setManagedRepositoryList( List<String> managedRepositoryList )
577     {
578         this.managedRepositoryList = managedRepositoryList;
579     }
580
581     public String getClassName()
582     {
583         return className;
584     }
585
586     public void setClassName( String className )
587     {
588         this.className = className;
589     }
590
591     public RepositorySearch getNexusSearch()
592     {
593         if( nexusSearch == null )
594         {
595             WebApplicationContext wac =
596                 WebApplicationContextUtils.getRequiredWebApplicationContext( ServletActionContext.getServletContext() );
597             nexusSearch = ( RepositorySearch ) wac.getBean( "nexusSearch" );
598         }
599         return nexusSearch;
600     }
601
602     public void setNexusSearch( RepositorySearch nexusSearch )
603     {
604         this.nexusSearch = nexusSearch;
605     }
606 }