Coverage Report - org.apache.maven.plugin.jira.ClassicJiraDownloader
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassicJiraDownloader
0%
0/140
0%
0/58
4.7
 
 1  
 package org.apache.maven.plugin.jira;
 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 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.settings.Proxy;
 38  
 import org.codehaus.plexus.util.IOUtil;
 39  
 import org.codehaus.plexus.util.StringUtils;
 40  
 
 41  
 import java.io.FileOutputStream;
 42  
 import java.io.IOException;
 43  
 import java.io.InputStream;
 44  
 import java.io.OutputStream;
 45  
 import java.net.URLEncoder;
 46  
 import java.util.Collections;
 47  
 import java.util.List;
 48  
 import java.util.Map;
 49  
 
 50  
 /**
 51  
  * Gets relevant issues for a JIRA report via HTTP/RSS.
 52  
  *
 53  
  * @author mfranken@xebia.com
 54  
  * @author jruiz@exist.com
 55  
  * @version $Id: ClassicJiraDownloader.java 1428311 2013-01-03 12:16:29Z dennisl $
 56  
  */
 57  
 public final class ClassicJiraDownloader
 58  
     extends AbstractJiraDownloader
 59  
 {
 60  
     public ClassicJiraDownloader()
 61  0
     {
 62  0
     }
 63  
 
 64  
     /**
 65  
      * Execute the query on the JIRA server.
 66  
      *
 67  
      * @throws Exception on error
 68  
      */
 69  
     public void doExecute()
 70  
         throws Exception
 71  
     {
 72  
         try
 73  
         {
 74  0
             HttpClient client = new HttpClient();
 75  
 
 76  
             // MCHANGES-89 Allow circular redirects
 77  0
             HttpClientParams clientParams = client.getParams();
 78  0
             clientParams.setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
 79  0
             clientParams.setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY ); //MCHANGES-237
 80  
 
 81  0
             HttpState state = new HttpState();
 82  
 
 83  0
             HostConfiguration hc = new HostConfiguration();
 84  
 
 85  0
             client.setHostConfiguration( hc );
 86  
 
 87  0
             client.setState( state );
 88  
 
 89  0
             String baseUrl = JiraHelper.getBaseUrl( project.getIssueManagement().getUrl() );
 90  
 
 91  0
             getLog().debug( "JIRA lives at: " + baseUrl );
 92  
             // Here we only need the host part of the URL
 93  0
             determineProxy( baseUrl, client );
 94  
 
 95  0
             prepareBasicAuthentication( client );
 96  
 
 97  0
             boolean jiraAuthenticationSuccessful = false;
 98  0
             if ( isJiraAuthenticationConfigured() )
 99  
             {
 100  
                 // Here we only need the parts up to and including the host part of the URL
 101  0
                 jiraAuthenticationSuccessful = doJiraAuthentication( client, baseUrl );
 102  
             }
 103  
 
 104  0
             if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
 105  
                 || !isJiraAuthenticationConfigured() )
 106  
             {
 107  0
                 String fullUrl = null;
 108  
 
 109  0
                 if ( useJql )
 110  
                 {
 111  0
                     fullUrl = getJqlQueryURL();
 112  
                 }
 113  
                 else
 114  
                 {
 115  0
                     fullUrl = getParameterBasedQueryURL( client );
 116  
                 }
 117  0
                 if ( log.isDebugEnabled() )
 118  
                 {
 119  0
                     log.debug( "download jira issues from url " + fullUrl );
 120  
                 }
 121  
 
 122  
                 // execute the GET
 123  0
                 download( client, fullUrl );
 124  
             }
 125  
         }
 126  0
         catch ( Exception e )
 127  
         {
 128  0
             if ( project.getIssueManagement() != null )
 129  
             {
 130  0
                 getLog().error( "Error accessing " + project.getIssueManagement().getUrl(), e );
 131  
             }
 132  
             else
 133  
             {
 134  0
                 getLog().error( "Error accessing mock project issues", e );
 135  
             }
 136  0
         }
 137  0
     }
 138  
 
 139  
     private String getJqlQueryURL()
 140  
     {
 141  
         // JQL is based on project names instead of project ID's
 142  0
         Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectName( project.getIssueManagement().getUrl() );
 143  0
         String jiraUrl = urlMap.get( "url" );
 144  0
         String jiraProject = urlMap.get( "project" );
 145  
 
 146  0
         if ( jiraProject == null )
 147  
         {
 148  0
             throw new RuntimeException( "The issue management URL in the POM does not include a JIRA project name" );
 149  
         }
 150  
         else
 151  
         {
 152  
             // create the URL for getting the proper issues from JIRA
 153  0
             String jqlQuery = new JqlQueryBuilder( log )
 154  
                 .project( jiraProject )
 155  
                 .fixVersion( getFixFor() )
 156  
                 .fixVersionIds( fixVersionIds )
 157  
                 .statusIds( statusIds )
 158  
                 .priorityIds( priorityIds )
 159  
                 .resolutionIds( resolutionIds )
 160  
                 .components( component )
 161  
                 .typeIds( typeIds )
 162  
                 .sortColumnNames( sortColumnNames )
 163  
                 .build();
 164  
 
 165  0
             String url = new UrlBuilder( jiraUrl, "sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml" )
 166  
                 .addParameter( "tempMax", nbEntriesMax )
 167  
                 .addParameter( "reset", "true" )
 168  
                 .addParameter( "jqlQuery", jqlQuery )
 169  
                 .build();
 170  
 
 171  0
             return url;
 172  
         }
 173  
     }
 174  
 
 175  
     private String getParameterBasedQueryURL( HttpClient client )
 176  
     {
 177  0
         Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
 178  0
         String jiraUrl = urlMap.get( "url" );
 179  0
         String jiraId = urlMap.get( "id" );
 180  
 
 181  0
         if ( jiraId == null || jiraId.length() == 0 )
 182  
         {
 183  0
             log.debug( "The JIRA URL " + project.getIssueManagement().getUrl()
 184  
                            + " doesn't include a pid, trying to extract it from JIRA." );
 185  0
             jiraId = JiraHelper.getPidFromJira( log, project.getIssueManagement().getUrl(), client );
 186  
         }
 187  
 
 188  0
         if ( jiraId == null )
 189  
         {
 190  0
             throw new RuntimeException( "The issue management URL in the POM does not include a pid,"
 191  
                                             + " and it was not possible to extract it from the page at that URL." );
 192  
         }
 193  
         else
 194  
         {
 195  
             // create the URL for getting the proper issues from JIRA
 196  0
             String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
 197  
 
 198  0
             if ( getFixFor() != null )
 199  
             {
 200  0
                 fullURL += "&fixfor=" + getFixFor();
 201  
             }
 202  
 
 203  0
             String createdFilter = new ParameterQueryBuilder( log )
 204  
                 .fixVersionIds( fixVersionIds )
 205  
                 .statusIds( statusIds )
 206  
                 .priorityIds( priorityIds )
 207  
                 .resolutionIds( resolutionIds )
 208  
                 .components( component )
 209  
                 .typeIds( typeIds )
 210  
                 .sortColumnNames( sortColumnNames )
 211  
                 .filter( filter )
 212  
                 .build();
 213  
 
 214  0
             if ( createdFilter.charAt( 0 ) != '&' )
 215  
             {
 216  0
                 fullURL += "&";
 217  
             }
 218  0
             fullURL += createdFilter;
 219  
 
 220  0
             fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
 221  
 
 222  0
             return fullURL;
 223  
         }
 224  
     }
 225  
 
 226  
     /**
 227  
      * Check and prepare for basic authentication.
 228  
      *
 229  
      * @param client The client to prepare
 230  
      */
 231  
     private void prepareBasicAuthentication( HttpClient client )
 232  
     {
 233  0
         if ( ( webUser != null ) && ( webUser.length() > 0 ) )
 234  
         {
 235  0
             client.getParams().setAuthenticationPreemptive( true );
 236  
 
 237  0
             Credentials defaultcreds = new UsernamePasswordCredentials( webUser, webPassword );
 238  
 
 239  0
             getLog().debug( "Using username: " + webUser + " for Basic Authentication." );
 240  
 
 241  0
             client.getState().setCredentials( new AuthScope( null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME ),
 242  
                                               defaultcreds );
 243  
         }
 244  0
     }
 245  
 
 246  
     /**
 247  
      * Authenticate against JIRA. This method relies on jiraUser and
 248  
      * jiraPassword being set. You can check this by calling
 249  
      * isJiraAuthenticationConfigured().
 250  
      *
 251  
      * @param client    the HttpClient
 252  
      * @param jiraUrl   the JIRA installation
 253  
      * @return <code>true</code> if the authentication was successful, otherwise <code>false</code>
 254  
      */
 255  
     private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
 256  
     {
 257  
         // log into JIRA if we have to
 258  0
         String loginUrl = null;
 259  
 
 260  0
         StringBuilder loginLink = new StringBuilder( jiraUrl );
 261  
 
 262  0
         loginLink.append( "/login.jsp?os_destination=/secure/" );
 263  
 
 264  
         try
 265  
         {
 266  0
             loginLink.append( "&os_username=" ).append( URLEncoder.encode( jiraUser, UTF_8 ) );
 267  
 
 268  0
             String password = null;
 269  0
             if ( jiraPassword != null )
 270  
             {
 271  0
                 password = StringUtils.repeat( "*", jiraPassword.length() );
 272  
             }
 273  0
             getLog().debug( "Login URL: " + loginLink + "&os_password=" + password );
 274  
 
 275  0
             loginLink.append( "&os_password=" ).append( URLEncoder.encode( jiraPassword, UTF_8 ) );
 276  
 
 277  0
             loginUrl = loginLink.toString();
 278  
 
 279  
             // execute the login
 280  0
             GetMethod loginGet = new GetMethod( loginUrl );
 281  
 
 282  0
             client.executeMethod( loginGet );
 283  
 
 284  0
             if ( loginSucceeded( loginGet ) )
 285  
             {
 286  0
                 getLog().debug( "Successfully logged in into JIRA." );
 287  0
                 return true;
 288  
             }
 289  
             else
 290  
             {
 291  0
                 getLog().warn( "Was unable to login into JIRA: wrong username and/or password." );
 292  
             }
 293  
         }
 294  0
         catch ( Exception e )
 295  
         {
 296  0
             if ( getLog().isDebugEnabled() )
 297  
             {
 298  0
                 getLog().error( "Error trying to login into JIRA.", e );
 299  
             }
 300  
             else
 301  
             {
 302  0
                 getLog().error( "Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage() );
 303  
             }
 304  0
         }
 305  0
         return false;
 306  
     }
 307  
 
 308  
     /**
 309  
      * Evaluate if the login attempt to JIRA was successful or not. We can't
 310  
      * use the status code because JIRA returns 200 even if the login fails.
 311  
      *
 312  
      * @param loginGet The method that was executed
 313  
      * @return <code>false</code> if we find an error message in the response body, otherwise <code>true</code>
 314  
      * @todo There must be a nicer way to know whether we were able to login or not
 315  
      */
 316  
     private boolean loginSucceeded( GetMethod loginGet )
 317  
         throws IOException
 318  
     {
 319  0
         final String loginFailureResponse = "your username and password are incorrect";
 320  
 
 321  0
         return loginGet.getResponseBodyAsString().indexOf( loginFailureResponse ) == -1;
 322  
     }
 323  
 
 324  
     /**
 325  
      * Setup proxy access if we have to.
 326  
      *
 327  
      * @param client  the HttpClient
 328  
      */
 329  
     private void determineProxy( String jiraUrl, HttpClient client )
 330  
     {
 331  
         // see whether there is any proxy defined in maven
 332  0
         Proxy proxy = null;
 333  
 
 334  0
         getProxyInfo( jiraUrl );
 335  
 
 336  0
         if ( proxyHost != null )
 337  
         {
 338  0
             client.getHostConfiguration().setProxy( proxyHost, proxyPort );
 339  
 
 340  0
             getLog().debug( "Using proxy: " + proxyHost + " at port " + proxyPort );
 341  
 
 342  0
             if ( proxyUser != null )
 343  
             {
 344  0
                 getLog().debug( "Using proxy user: " + proxyUser );
 345  
 
 346  0
                 client.getState().setProxyCredentials(
 347  
                     new AuthScope( null, AuthScope.ANY_PORT, null,
 348  
                                    AuthScope.ANY_SCHEME ),
 349  
                     new UsernamePasswordCredentials( proxyUser, proxyPass ) );
 350  
             }
 351  
         }
 352  0
     }
 353  
 
 354  
     /**
 355  
      * Downloads the given link using the configured HttpClient, possibly following redirects.
 356  
      *
 357  
      * @param cl     the HttpClient
 358  
      * @param link   the URL to JIRA
 359  
      */
 360  
     private void download( final HttpClient cl, final String link )
 361  
     {
 362  
         try
 363  
         {
 364  0
             GetMethod gm = new GetMethod( link );
 365  
 
 366  0
             getLog().info( "Downloading from JIRA at: " + link );
 367  
 
 368  0
             gm.setFollowRedirects( true );
 369  
 
 370  0
             cl.executeMethod( gm );
 371  
 
 372  0
             StatusLine sl = gm.getStatusLine();
 373  
 
 374  0
             if ( sl == null )
 375  
             {
 376  0
                 getLog().error( "Unknown error validating link: " + link );
 377  
 
 378  0
                 return;
 379  
             }
 380  
 
 381  
             // if we get a redirect, do so
 382  0
             if ( gm.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY )
 383  
             {
 384  0
                 Header locationHeader = gm.getResponseHeader( "Location" );
 385  
 
 386  0
                 if ( locationHeader == null )
 387  
                 {
 388  0
                     getLog().warn( "Site sent redirect, but did not set Location header" );
 389  
                 }
 390  
                 else
 391  
                 {
 392  0
                     String newLink = locationHeader.getValue();
 393  
 
 394  0
                     getLog().debug( "Following redirect to " + newLink );
 395  
 
 396  0
                     download( cl, newLink );
 397  
                 }
 398  
             }
 399  
 
 400  0
             if ( gm.getStatusCode() == HttpStatus.SC_OK )
 401  
             {
 402  0
                 final InputStream responseBodyStream = gm.getResponseBodyAsStream();
 403  
 
 404  0
                 if ( !output.getParentFile().exists() )
 405  
                 {
 406  0
                     output.getParentFile().mkdirs();
 407  
                 }
 408  
 
 409  
                 // write the response to file
 410  0
                 OutputStream out = null;
 411  
                 try
 412  
                 {
 413  0
                     out = new FileOutputStream( output );
 414  0
                     IOUtil.copy( responseBodyStream, out );
 415  
                 }
 416  
                 finally
 417  
                 {
 418  0
                     IOUtil.close( out );
 419  0
                     IOUtil.close( responseBodyStream );
 420  0
                 }
 421  
 
 422  0
                 getLog().debug( "Downloading from JIRA was successful" );
 423  0
             }
 424  
             else
 425  
             {
 426  0
                 getLog().warn( "Downloading from JIRA failed. Received: [" + gm.getStatusCode() + "]" );
 427  
             }
 428  
         }
 429  0
         catch ( HttpException e )
 430  
         {
 431  0
             if ( getLog().isDebugEnabled() )
 432  
             {
 433  0
                 getLog().error( "Error downloading issues from JIRA:", e );
 434  
             }
 435  
             else
 436  
             {
 437  0
                 getLog().error( "Error downloading issues from JIRA url: " + e.getLocalizedMessage() );
 438  
 
 439  
             }
 440  
         }
 441  0
         catch ( IOException e )
 442  
         {
 443  0
             if ( getLog().isDebugEnabled() )
 444  
             {
 445  0
                 getLog().error( "Error downloading issues from JIRA:", e );
 446  
             }
 447  
             else
 448  
             {
 449  0
                 getLog().error( "Error downloading issues from JIRA. Cause is " + e.getLocalizedMessage() );
 450  
             }
 451  0
         }
 452  0
     }
 453  
 
 454  
     public List<Issue> getIssueList()
 455  
         throws MojoExecutionException
 456  
     {
 457  0
         if ( output.isFile() )
 458  
         {
 459  0
             JiraXML jira = new JiraXML( log, jiraDatePattern );
 460  0
             jira.parseXML( output );
 461  0
             getLog().info( "The JIRA version is '" + jira.getJiraVersion() + "'" );
 462  0
             return jira.getIssueList();
 463  
         }
 464  
         else
 465  
         {
 466  0
             getLog().warn( "JIRA file " + output.getPath() + " doesn't exist." );
 467  0
             return Collections.emptyList();
 468  
         }
 469  
     }
 470  
 
 471  
 }