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