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