]> source.dussan.org Git - archiva.git/blob
939dbc51a5eb2f61a9a89782baef8768c612eadc
[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
50 /**
51  * Search all indexed fields by the given criteria.
52  *
53  * @plexus.component role="com.opensymphony.xwork2.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 List<String> managedRepositoryList;
111
112     private String groupId;
113
114     private String artifactId;
115
116     private String version;
117
118     private String className;
119
120     private int rowCount = 30;
121
122     private String repositoryId;
123
124     private boolean fromFilterSearch;
125
126     private boolean filterSearch = false;
127
128     private boolean fromResultsPage;
129
130     private int num;
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         return SUCCESS;
278     }
279
280     public String findArtifact()
281         throws Exception
282     {
283         // TODO: give action message if indexing is in progress
284
285         if ( StringUtils.isBlank( q ) )
286         {
287             addActionError( "Unable to search for a blank checksum" );
288             return INPUT;
289         }
290
291         Constraint constraint = new ArtifactsByChecksumConstraint( q );
292         databaseResults = dao.getArtifactDAO().queryArtifacts( constraint );
293
294         if ( databaseResults.isEmpty() )
295         {
296             addActionError( "No results found" );
297             return INPUT;
298         }
299
300         if ( databaseResults.size() == 1 )
301         {
302             // 1 hit? return it's information directly!
303             return ARTIFACT;
304         }
305
306         return RESULTS;
307     }
308     
309     public String doInput()
310     {
311         return INPUT;
312     }
313
314     private String getPrincipal()
315     {
316         return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
317     }
318
319     private List<String> getObservableRepos()
320     {
321         try
322         {
323             return userRepositories.getObservableRepositoryIds( getPrincipal() );
324         }
325         catch ( PrincipalNotFoundException e )
326         {
327             getLogger().warn( e.getMessage(), e );
328         }
329         catch ( AccessDeniedException e )
330         {
331             getLogger().warn( e.getMessage(), e );
332             // TODO: pass this onto the screen.
333         }
334         catch ( ArchivaSecurityException e )
335         {
336             getLogger().warn( e.getMessage(), e );
337         }
338         return Collections.emptyList();
339     }
340
341     private void buildCompleteQueryString( String searchTerm )
342     {
343         if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
344         {
345             searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
346         }
347
348         if ( completeQueryString == null || "".equals( completeQueryString ) )
349         {
350             completeQueryString = searchTerm;
351         }
352         else
353         {
354             completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
355         }
356     }
357
358     private List<String> parseCompleteQueryString()
359     {
360         List<String> parsedCompleteQueryString = new ArrayList<String>();
361         String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
362         CollectionUtils.addAll( parsedCompleteQueryString, parsed );
363
364         return parsedCompleteQueryString;
365     }
366
367     private boolean isEqualToPreviousSearchTerm( String searchTerm )
368     {
369         if ( !"".equals( completeQueryString ) )
370         {
371             String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
372             if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
373             {
374                 return true;
375             }
376         }
377
378         return false;
379     }
380
381     public String getQ()
382     {
383         return q;
384     }
385
386     public void setQ( String q )
387     {
388         this.q = q;
389     }
390
391     public SearchResults getResults()
392     {
393         return results;
394     }
395
396     public List getDatabaseResults()
397     {
398         return databaseResults;
399     }
400
401     public void setCurrentPage( int page )
402     {
403         this.currentPage = page;
404     }
405
406     public int getCurrentPage()
407     {
408         return currentPage;
409     }
410
411     public int getTotalPages()
412     {
413         return totalPages;
414     }
415
416     public void setTotalPages( int totalPages )
417     {
418         this.totalPages = totalPages;
419     }
420
421     public boolean isSearchResultsOnly()
422     {
423         return searchResultsOnly;
424     }
425
426     public void setSearchResultsOnly( boolean searchResultsOnly )
427     {
428         this.searchResultsOnly = searchResultsOnly;
429     }
430
431     public String getCompleteQueryString()
432     {
433         return completeQueryString;
434     }
435
436     public void setCompleteQueryString( String completeQueryString )
437     {
438         this.completeQueryString = completeQueryString;
439     }
440
441     public ArchivaConfiguration getArchivaConfiguration()
442     {
443         return archivaConfiguration;
444     }
445
446     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
447     {
448         this.archivaConfiguration = archivaConfiguration;
449     }
450
451     public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
452     {
453         return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
454     }
455
456     public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
457     {
458         this.managedRepositories = managedRepositories;
459     }
460
461     public String getGroupId()
462     {
463         return groupId;
464     }
465
466     public void setGroupId( String groupId )
467     {
468         this.groupId = groupId;
469     }
470
471     public String getArtifactId()
472     {
473         return artifactId;
474     }
475
476     public void setArtifactId( String artifactId )
477     {
478         this.artifactId = artifactId;
479     }
480
481     public String getVersion()
482     {
483         return version;
484     }
485
486     public void setVersion( String version )
487     {
488         this.version = version;
489     }
490
491     public int getRowCount()
492     {
493         return rowCount;
494     }
495
496     public void setRowCount( int rowCount )
497     {
498         this.rowCount = rowCount;
499     }
500
501     public boolean isFilterSearch()
502     {
503         return filterSearch;
504     }
505
506     public void setFilterSearch( boolean filterSearch )
507     {
508         this.filterSearch = filterSearch;
509     }
510
511     public String getRepositoryId()
512     {
513         return repositoryId;
514     }
515
516     public void setRepositoryId( String repositoryId )
517     {
518         this.repositoryId = repositoryId;
519     }
520
521     public List<String> getManagedRepositoryList()
522     {
523         return managedRepositoryList;
524     }
525
526     public void setManagedRepositoryList( List<String> managedRepositoryList )
527     {
528         this.managedRepositoryList = managedRepositoryList;
529     }
530
531     public String getClassName()
532     {
533         return className;
534     }
535
536     public void setClassName( String className )
537     {
538         this.className = className;
539     }
540 }