1 package org.apache.maven.plugin.jira;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.httpclient.Credentials;
23 import org.apache.commons.httpclient.Header;
24 import org.apache.commons.httpclient.HostConfiguration;
25 import org.apache.commons.httpclient.HttpClient;
26 import org.apache.commons.httpclient.HttpException;
27 import org.apache.commons.httpclient.HttpState;
28 import org.apache.commons.httpclient.HttpStatus;
29 import org.apache.commons.httpclient.StatusLine;
30 import org.apache.commons.httpclient.UsernamePasswordCredentials;
31 import org.apache.commons.httpclient.params.HttpClientParams;
32 import org.apache.commons.httpclient.auth.AuthScope;
33 import org.apache.commons.httpclient.methods.GetMethod;
34 import org.apache.maven.plugin.logging.Log;
35 import org.apache.maven.project.MavenProject;
36 import org.apache.maven.settings.Proxy;
37 import org.apache.maven.settings.Settings;
38 import org.apache.maven.wagon.proxy.ProxyInfo;
39 import org.codehaus.plexus.util.IOUtil;
40 import org.codehaus.plexus.util.StringUtils;
41
42 import java.io.File;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.OutputStream;
47 import java.net.URLEncoder;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Locale;
52 import java.util.Map;
53
54
55
56
57
58
59
60
61
62
63 public abstract class AbstractJiraDownloader
64 {
65 private static final String UTF_8 = "UTF-8";
66
67
68 private Log log;
69
70 private File output;
71
72 private int nbEntriesMax;
73
74 private String filter;
75
76 private String fixVersionIds;
77
78 private String statusIds;
79
80 private String resolutionIds;
81
82 private String priorityIds;
83
84 private String component;
85
86 private String typeIds;
87
88 private String sortColumnNames;
89
90 private String jiraUser;
91
92 private String jiraPassword;
93
94 private String webUser;
95
96 private String webPassword;
97
98 private MavenProject project;
99
100 private Settings settings;
101
102 protected final Map statusMap = new HashMap( 8 );
103
104 protected final Map resolutionMap = new HashMap( 8 );
105
106 protected final Map priorityMap = new HashMap( 8 );
107
108 protected final Map typeMap = new HashMap( 8 );
109
110 private String jiraDatePattern;
111
112
113
114
115
116
117 private String createFilter()
118 {
119
120 if ( ( this.filter != null ) && ( this.filter.length() > 0 ) )
121 {
122 return this.filter;
123 }
124
125 StringBuffer localFilter = new StringBuffer( 16 );
126
127
128 if ( fixVersionIds != null )
129 {
130 String[] fixVersions = fixVersionIds.split( "," );
131
132 for ( int i = 0; i < fixVersions.length; i++ )
133 {
134 if ( fixVersions[i].length() > 0 )
135 {
136 localFilter.append( "&fixfor=" ).append( fixVersions[i].trim() );
137 }
138 }
139 }
140
141
142 if ( statusIds != null )
143 {
144 String[] stats = statusIds.split( "," );
145
146 for ( int i = 0; i < stats.length; i++ )
147 {
148 String statusParam = (String) statusMap.get( stats[i] );
149
150 if ( statusParam != null )
151 {
152 localFilter.append( "&statusIds=" ).append( statusParam );
153 }
154 }
155 }
156
157
158 if ( priorityIds != null )
159 {
160 String[] prios = priorityIds.split( "," );
161
162 for ( int i = 0; i < prios.length; i++ )
163 {
164 String priorityParam = (String) priorityMap.get( prios[i] );
165
166 if ( priorityParam != null )
167 {
168 localFilter.append( "&priorityIds=" ).append( priorityParam );
169 }
170 }
171 }
172
173
174 if ( resolutionIds != null )
175 {
176 String[] resos = resolutionIds.split( "," );
177
178 for ( int i = 0; i < resos.length; i++ )
179 {
180 String resoParam = (String) resolutionMap.get( resos[i] );
181
182 if ( resoParam != null )
183 {
184 localFilter.append( "&resolutionIds=" ).append( resoParam );
185 }
186 }
187 }
188
189
190 if ( component != null )
191 {
192 String[] components = component.split( "," );
193
194 for ( int i = 0; i < components.length; i++ )
195 {
196 if ( components[i].length() > 0 )
197 {
198 localFilter.append( "&component=" ).append( components[i] );
199 }
200 }
201 }
202
203
204 if ( typeIds != null )
205 {
206 String[] types = typeIds.split( "," );
207
208 for ( int i = 0; i < types.length; i++ )
209 {
210 String typeParam = (String) typeMap.get( types[i].trim() );
211
212 if ( typeParam != null )
213 {
214 localFilter.append( "&type=" ).append( typeParam );
215 }
216 }
217 }
218
219
220 int validSortColumnNames = 0;
221 if ( sortColumnNames != null )
222 {
223 String[] sortColumnNamesArray = sortColumnNames.split( "," );
224
225 for ( int i = sortColumnNamesArray.length - 1; i >= 0; i-- )
226 {
227 String lowerColumnName = sortColumnNamesArray[i].trim().toLowerCase( Locale.ENGLISH );
228 boolean descending = false;
229 String fieldName = null;
230 if ( lowerColumnName.endsWith( "desc" ) )
231 {
232 descending = true;
233 lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 4 ).trim();
234 }
235 else if ( lowerColumnName.endsWith( "asc" ) )
236 {
237 descending = false;
238 lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 3 ).trim();
239 }
240
241 if ( "key".equals( lowerColumnName ) )
242 {
243 fieldName = "issuekey";
244 }
245 else if ( "summary".equals( lowerColumnName ) )
246 {
247 fieldName = lowerColumnName;
248 }
249 else if ( "status".equals( lowerColumnName ) )
250 {
251 fieldName = lowerColumnName;
252 }
253 else if ( "resolution".equals( lowerColumnName ) )
254 {
255 fieldName = lowerColumnName;
256 }
257 else if ( "assignee".equals( lowerColumnName ) )
258 {
259 fieldName = lowerColumnName;
260 }
261 else if ( "reporter".equals( lowerColumnName ) )
262 {
263 fieldName = lowerColumnName;
264 }
265 else if ( "type".equals( lowerColumnName ) )
266 {
267 fieldName = "issuetype";
268 }
269 else if ( "priority".equals( lowerColumnName ) )
270 {
271 fieldName = lowerColumnName;
272 }
273 else if ( "version".equals( lowerColumnName ) )
274 {
275 fieldName = "versions";
276 }
277 else if ( "fix version".equals( lowerColumnName ) )
278 {
279 fieldName = "fixVersions";
280 }
281 else if ( "component".equals( lowerColumnName ) )
282 {
283 fieldName = "components";
284 }
285 else if ( "created".equals( lowerColumnName ) )
286 {
287 fieldName = lowerColumnName;
288 }
289 else if ( "updated".equals( lowerColumnName ) )
290 {
291 fieldName = lowerColumnName;
292 }
293 if ( fieldName != null )
294 {
295 localFilter.append( "&sorter/field=" );
296 localFilter.append( fieldName );
297 localFilter.append( "&sorter/order=" );
298 localFilter.append( descending ? "DESC" : "ASC" );
299 validSortColumnNames++;
300 }
301 else
302 {
303
304 getLog().error(
305 "maven-changes-plugin: The configured value '" + lowerColumnName
306 + "' for sortColumnNames is not correct." );
307 }
308 }
309 }
310 if ( validSortColumnNames == 0 )
311 {
312
313 getLog().error(
314 "maven-changes-plugin: None of the configured sortColumnNames '" + sortColumnNames + "' are correct." );
315 }
316
317
318 return localFilter.toString();
319 }
320
321
322
323
324
325
326 public void doExecute()
327 throws Exception
328 {
329 try
330 {
331 HttpClient client = new HttpClient();
332
333
334 HttpClientParams clientParams = client.getParams();
335 clientParams.setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
336
337 HttpState state = new HttpState();
338
339 HostConfiguration hc = new HostConfiguration();
340
341 client.setHostConfiguration( hc );
342
343 client.setState( state );
344
345 Map urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
346
347 String jiraUrl = (String) urlMap.get( "url" );
348 getLog().debug( "JIRA lives at: " + jiraUrl );
349
350 String jiraId = (String) urlMap.get( "id" );
351
352 determineProxy( jiraUrl, client );
353
354 prepareBasicAuthentication( client );
355
356 boolean jiraAuthenticationSuccessful = false;
357 if ( isJiraAuthenticationConfigured() )
358 {
359 jiraAuthenticationSuccessful = doJiraAuthentication( client, jiraUrl );
360 }
361
362 if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
363 || !isJiraAuthenticationConfigured() )
364 {
365 if ( jiraId == null || jiraId.length() == 0 )
366 {
367 log.debug( "The JIRA URL " + project.getIssueManagement().getUrl()
368 + " doesn't include a pid, trying to extract it from JIRA." );
369 jiraId = JiraHelper.getPidFromJira( log, project.getIssueManagement().getUrl(), client );
370 }
371
372 if ( jiraId == null )
373 {
374 getLog().error( "The issue management URL in the POM does not include a pid,"
375 + " and it was not possible to extract it from the page at that URL." );
376 }
377 else
378 {
379
380 String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
381
382 if ( getFixFor() != null )
383 {
384 fullURL += "&fixfor=" + getFixFor();
385 }
386
387 String createdFilter = createFilter();
388 if ( createdFilter.charAt( 0 ) != '&' )
389 {
390 fullURL += "&";
391 }
392 fullURL += createdFilter;
393
394 fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
395
396 if ( log.isDebugEnabled() )
397 {
398 log.debug( "download jira issues from url " + fullURL );
399 }
400
401
402 download( client, fullURL );
403 }
404 }
405 }
406 catch ( Exception e )
407 {
408 getLog().error( "Error accessing " + project.getIssueManagement().getUrl(), e );
409 }
410 }
411
412
413
414
415
416
417 protected String getFixFor()
418 {
419 return null;
420 }
421
422
423
424
425
426
427 private void prepareBasicAuthentication( HttpClient client )
428 {
429 if ( ( webUser != null ) && ( webUser.length() > 0 ) )
430 {
431 client.getParams().setAuthenticationPreemptive( true );
432
433 Credentials defaultcreds = new UsernamePasswordCredentials( webUser, webPassword );
434
435 getLog().debug( "Using username: " + webUser + " for Basic Authentication." );
436
437 client.getState().setCredentials( new AuthScope( null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME ),
438 defaultcreds );
439 }
440 }
441
442
443
444
445
446
447
448
449
450
451 private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
452 {
453
454 String loginUrl = null;
455
456 StringBuffer loginLink = new StringBuffer( jiraUrl );
457
458 loginLink.append( "/login.jsp?os_destination=/secure/" );
459
460 try
461 {
462 loginLink.append( "&os_username=" ).append( URLEncoder.encode( jiraUser, UTF_8 ) );
463
464 String password = null;
465 if ( jiraPassword != null )
466 {
467 password = StringUtils.repeat( "*", jiraPassword.length() );
468 }
469 getLog().debug( "Login URL: " + loginLink + "&os_password=" + password );
470
471 loginLink.append( "&os_password=" ).append( URLEncoder.encode( jiraPassword, UTF_8 ) );
472
473 loginUrl = loginLink.toString();
474
475
476 GetMethod loginGet = new GetMethod( loginUrl );
477
478 client.executeMethod( loginGet );
479
480 if ( loginSucceeded( loginGet ) )
481 {
482 getLog().debug( "Successfully logged in into JIRA." );
483 return true;
484 }
485 else
486 {
487 getLog().warn( "Was unable to login into JIRA: wrong username and/or password." );
488 }
489 }
490 catch ( Exception e )
491 {
492 if ( getLog().isDebugEnabled() )
493 {
494 getLog().error( "Error trying to login into JIRA.", e );
495 }
496 else
497 {
498 getLog().error( "Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage() );
499 }
500 }
501 return false;
502 }
503
504
505
506
507
508
509 private boolean isJiraAuthenticationConfigured()
510 {
511 return ( jiraUser != null ) && ( jiraUser.length() > 0 ) && ( jiraPassword != null );
512 }
513
514
515
516
517
518
519
520
521
522 private boolean loginSucceeded( GetMethod loginGet )
523 throws IOException
524 {
525 final String loginFailureResponse = "your username and password are incorrect";
526
527 return loginGet.getResponseBodyAsString().indexOf( loginFailureResponse ) == -1;
528 }
529
530
531
532
533
534
535 private void determineProxy( String jiraUrl, HttpClient client )
536 {
537
538 Proxy proxy = null;
539
540 String proxyHost = null;
541
542 int proxyPort = 0;
543
544 String proxyUser = null;
545
546 String proxyPass = null;
547
548 if ( project == null )
549 {
550 getLog().error( "No project set. No proxy info available." );
551
552 return;
553 }
554
555 if ( settings != null )
556 {
557 proxy = settings.getActiveProxy();
558 }
559
560 if ( proxy != null )
561 {
562
563 ProxyInfo proxyInfo = new ProxyInfo();
564 proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
565
566
567
568
569
570 if ( JiraHelper.validateNonProxyHosts( proxyInfo, jiraUrl ) )
571 {
572 return;
573 }
574
575 proxyHost = settings.getActiveProxy().getHost();
576
577 proxyPort = settings.getActiveProxy().getPort();
578
579 proxyUser = settings.getActiveProxy().getUsername();
580
581 proxyPass = settings.getActiveProxy().getPassword();
582
583 getLog().debug( proxyPass );
584 }
585
586 if ( proxyHost != null )
587 {
588 client.getHostConfiguration().setProxy( proxyHost, proxyPort );
589
590 getLog().debug( "Using proxy: " + proxyHost + " at port " + proxyPort );
591
592 if ( proxyUser != null )
593 {
594 getLog().debug( "Using proxy user: " + proxyUser );
595
596 client.getState().setProxyCredentials(
597 new AuthScope( null, AuthScope.ANY_PORT, null,
598 AuthScope.ANY_SCHEME ),
599 new UsernamePasswordCredentials( proxyUser, proxyPass ) );
600 }
601 }
602 }
603
604
605
606
607
608
609
610 private void download( final HttpClient cl, final String link )
611 {
612 try
613 {
614 GetMethod gm = new GetMethod( link );
615
616 getLog().info( "Downloading from JIRA at: " + link );
617
618 gm.setFollowRedirects( true );
619
620 cl.executeMethod( gm );
621
622 StatusLine sl = gm.getStatusLine();
623
624 if ( sl == null )
625 {
626 getLog().error( "Unknown error validating link: " + link );
627
628 return;
629 }
630
631
632 if ( gm.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY )
633 {
634 Header locationHeader = gm.getResponseHeader( "Location" );
635
636 if ( locationHeader == null )
637 {
638 getLog().warn( "Site sent redirect, but did not set Location header" );
639 }
640 else
641 {
642 String newLink = locationHeader.getValue();
643
644 getLog().debug( "Following redirect to " + newLink );
645
646 download( cl, newLink );
647 }
648 }
649
650 if ( gm.getStatusCode() == HttpStatus.SC_OK )
651 {
652 final InputStream responseBodyStream = gm.getResponseBodyAsStream();
653
654 if ( !output.getParentFile().exists() )
655 {
656 output.getParentFile().mkdirs();
657 }
658
659
660 OutputStream out = null;
661 try
662 {
663 out = new FileOutputStream( output );
664 IOUtil.copy( responseBodyStream, out );
665 }
666 finally
667 {
668 IOUtil.close( out );
669 IOUtil.close( responseBodyStream );
670 }
671
672 getLog().debug( "Downloading from JIRA was successful" );
673 }
674 else
675 {
676 getLog().warn( "Downloading from JIRA failed. Received: [" + gm.getStatusCode() + "]" );
677 }
678 }
679 catch ( HttpException e )
680 {
681 if ( getLog().isDebugEnabled() )
682 {
683 getLog().error( "Error downloading issues from JIRA:", e );
684 }
685 else
686 {
687 getLog().error( "Error downloading issues from JIRA url: " + e.getLocalizedMessage() );
688
689 }
690 }
691 catch ( IOException e )
692 {
693 if ( getLog().isDebugEnabled() )
694 {
695 getLog().error( "Error downloading issues from JIRA:", e );
696 }
697 else
698 {
699 getLog().error( "Error downloading issues from JIRA. Cause is " + e.getLocalizedMessage() );
700 }
701 }
702 }
703
704 public List getIssueList()
705 {
706 if ( output.isFile() )
707 {
708 JiraXML jira = new JiraXML( log, jiraDatePattern );
709 jira.parseXML( output );
710 getLog().info( "The JIRA version is '" + jira.getJiraVersion() + "'" );
711 return jira.getIssueList();
712 }
713 else
714 {
715 getLog().warn( "JIRA file " + output.getPath() + " doesn't exist." );
716 return Collections.EMPTY_LIST;
717 }
718 }
719
720 public void setJiraDatePattern( String jiraDatePattern )
721 {
722 this.jiraDatePattern = jiraDatePattern;
723 }
724
725
726
727
728
729
730 public void setOutput( File thisOutput )
731 {
732 this.output = thisOutput;
733 }
734
735 public File getOutput()
736 {
737 return this.output;
738 }
739
740
741
742
743
744
745 public void setMavenProject( Object thisProject )
746 {
747 this.project = (MavenProject) thisProject;
748 }
749
750
751
752
753
754
755 public void setNbEntries( final int nbEntries )
756 {
757 nbEntriesMax = nbEntries;
758 }
759
760
761
762
763
764
765 public void setStatusIds( String thisStatusIds )
766 {
767 statusIds = thisStatusIds;
768 }
769
770
771
772
773
774
775 public void setPriorityIds( String thisPriorityIds )
776 {
777 priorityIds = thisPriorityIds;
778 }
779
780
781
782
783
784
785 public void setResolutionIds( String thisResolutionIds )
786 {
787 resolutionIds = thisResolutionIds;
788 }
789
790
791
792
793
794
795 public void setSortColumnNames( String thisSortColumnNames )
796 {
797 sortColumnNames = thisSortColumnNames;
798 }
799
800
801
802
803
804
805 public void setWebPassword( String thisWebPassword )
806 {
807 this.webPassword = thisWebPassword;
808 }
809
810
811
812
813
814
815 public void setWebUser( String thisWebUser )
816 {
817 this.webUser = thisWebUser;
818 }
819
820
821
822
823
824
825 public void setJiraPassword( final String thisJiraPassword )
826 {
827 this.jiraPassword = thisJiraPassword;
828 }
829
830
831
832
833
834
835 public void setJiraUser( String thisJiraUser )
836 {
837 this.jiraUser = thisJiraUser;
838 }
839
840
841
842
843
844
845 public void setFilter( String thisFilter )
846 {
847 this.filter = thisFilter;
848 }
849
850
851
852
853
854
855 public void setComponent( String theseComponents )
856 {
857 this.component = theseComponents;
858 }
859
860
861
862
863
864
865 public void setFixVersionIds( String theseFixVersionIds )
866 {
867 this.fixVersionIds = theseFixVersionIds;
868 }
869
870
871
872
873
874
875 public void setTypeIds( String theseTypeIds )
876 {
877 typeIds = theseTypeIds;
878 }
879
880 public void setLog( Log log )
881 {
882 this.log = log;
883 }
884
885 private Log getLog()
886 {
887 return log;
888 }
889
890 public void setSettings( Settings settings )
891 {
892 this.settings = settings;
893 }
894 }