blob: 162290fd0704a18b7c6167fb1ff7c8bd7bade74d [file] [log] [blame]
package org.apache.maven.plugin.jira;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.StatusLine;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.issues.Issue;
import org.apache.maven.settings.Proxy;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Gets relevant issues for a JIRA report via HTTP/RSS.
*
* @author mfranken@xebia.com
* @author jruiz@exist.com
* @version $Id$
*/
public final class ClassicJiraDownloader
extends AbstractJiraDownloader
{
public ClassicJiraDownloader()
{
}
/**
* Execute the query on the JIRA server.
*
* @throws Exception on error
*/
public void doExecute()
throws Exception
{
try
{
HttpClient client = new HttpClient();
// MCHANGES-89 Allow circular redirects
HttpClientParams clientParams = client.getParams();
clientParams.setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
clientParams.setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY ); //MCHANGES-237
HttpState state = new HttpState();
HostConfiguration hc = new HostConfiguration();
client.setHostConfiguration( hc );
client.setState( state );
String baseUrl = JiraHelper.getBaseUrl( project.getIssueManagement().getUrl() );
getLog().debug( "JIRA lives at: " + baseUrl );
// Here we only need the host part of the URL
determineProxy( baseUrl, client );
prepareBasicAuthentication( client );
boolean jiraAuthenticationSuccessful = false;
if ( isJiraAuthenticationConfigured() )
{
// Here we only need the parts up to and including the host part of the URL
jiraAuthenticationSuccessful = doJiraAuthentication( client, baseUrl );
}
if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
|| !isJiraAuthenticationConfigured() )
{
String fullUrl;
if ( useJql )
{
fullUrl = getJqlQueryURL();
}
else
{
fullUrl = getParameterBasedQueryURL( client );
}
if ( log.isDebugEnabled() )
{
log.debug( "download jira issues from url " + fullUrl );
}
// execute the GET
download( client, fullUrl );
}
}
catch ( Exception e )
{
if ( project.getIssueManagement() != null )
{
getLog().error( "Error accessing " + project.getIssueManagement().getUrl(), e );
}
else
{
getLog().error( "Error accessing mock project issues", e );
}
}
}
private String getJqlQueryURL()
{
// JQL is based on project names instead of project ID's
Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectName( project.getIssueManagement().getUrl() );
String jiraUrl = urlMap.get( "url" );
String jiraProject = urlMap.get( "project" );
if ( jiraProject == null )
{
throw new RuntimeException( "The issue management URL in the POM does not include a JIRA project name" );
}
else
{
// create the URL for getting the proper issues from JIRA
String jqlQuery = new JqlQueryBuilder( log )
.project( jiraProject )
.fixVersion( getFixFor() )
.fixVersionIds( fixVersionIds )
.statusIds( statusIds )
.priorityIds( priorityIds )
.resolutionIds( resolutionIds )
.components( component )
.typeIds( typeIds )
.sortColumnNames( sortColumnNames )
.build();
String url = new UrlBuilder( jiraUrl, "sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml" )
.addParameter( "tempMax", nbEntriesMax )
.addParameter( "reset", "true" )
.addParameter( "jqlQuery", jqlQuery )
.build();
return url;
}
}
private String getParameterBasedQueryURL( HttpClient client )
{
Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
String jiraUrl = urlMap.get( "url" );
String jiraId = urlMap.get( "id" );
if ( jiraId == null || jiraId.length() == 0 )
{
log.debug( "The JIRA URL " + project.getIssueManagement().getUrl()
+ " doesn't include a pid, trying to extract it from JIRA." );
jiraId = JiraHelper.getPidFromJira( log, project.getIssueManagement().getUrl(), client );
}
if ( jiraId == null )
{
throw new RuntimeException( "The issue management URL in the POM does not include a pid,"
+ " and it was not possible to extract it from the page at that URL." );
}
else
{
// create the URL for getting the proper issues from JIRA
String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
if ( getFixFor() != null )
{
fullURL += "&fixfor=" + getFixFor();
}
String createdFilter = new ParameterQueryBuilder( log )
.fixVersionIds( fixVersionIds )
.statusIds( statusIds )
.priorityIds( priorityIds )
.resolutionIds( resolutionIds )
.components( component )
.typeIds( typeIds )
.sortColumnNames( sortColumnNames )
.filter( filter )
.build();
if ( createdFilter.charAt( 0 ) != '&' )
{
fullURL += "&";
}
fullURL += createdFilter;
fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
return fullURL;
}
}
/**
* Check and prepare for basic authentication.
*
* @param client The client to prepare
*/
private void prepareBasicAuthentication( HttpClient client )
{
if ( ( webUser != null ) && ( webUser.length() > 0 ) )
{
client.getParams().setAuthenticationPreemptive( true );
Credentials defaultcreds = new UsernamePasswordCredentials( webUser, webPassword );
getLog().debug( "Using username: " + webUser + " for Basic Authentication." );
client.getState().setCredentials( new AuthScope( null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME ),
defaultcreds );
}
}
/**
* Authenticate against JIRA. This method relies on jiraUser and
* jiraPassword being set. You can check this by calling
* isJiraAuthenticationConfigured().
*
* @param client the HttpClient
* @param jiraUrl the JIRA installation
* @return <code>true</code> if the authentication was successful, otherwise <code>false</code>
*/
private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
{
// log into JIRA if we have to
String loginUrl;
StringBuilder loginLink = new StringBuilder( jiraUrl );
loginLink.append( "/login.jsp?os_destination=/secure/" );
try
{
loginLink.append( "&os_username=" ).append( URLEncoder.encode( jiraUser, UTF_8 ) );
String password = null;
if ( jiraPassword != null )
{
password = StringUtils.repeat( "*", jiraPassword.length() );
}
getLog().debug( "Login URL: " + loginLink + "&os_password=" + password );
loginLink.append( "&os_password=" ).append( URLEncoder.encode( jiraPassword, UTF_8 ) );
loginUrl = loginLink.toString();
// execute the login
GetMethod loginGet = new GetMethod( loginUrl );
client.executeMethod( loginGet );
if ( loginSucceeded( loginGet ) )
{
getLog().debug( "Successfully logged in into JIRA." );
return true;
}
else
{
getLog().warn( "Was unable to login into JIRA: wrong username and/or password." );
}
}
catch ( Exception e )
{
if ( getLog().isDebugEnabled() )
{
getLog().error( "Error trying to login into JIRA.", e );
}
else
{
getLog().error( "Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage() );
}
}
return false;
}
/**
* Evaluate if the login attempt to JIRA was successful or not. We can't
* use the status code because JIRA returns 200 even if the login fails.
*
* @param loginGet The method that was executed
* @return <code>false</code> if we find an error message in the response body, otherwise <code>true</code>
* @todo There must be a nicer way to know whether we were able to login or not
*/
private boolean loginSucceeded( GetMethod loginGet )
throws IOException
{
final String loginFailureResponse = "your username and password are incorrect";
return !loginGet.getResponseBodyAsString().contains( loginFailureResponse );
}
/**
* Setup proxy access if we have to.
*
* @param client the HttpClient
*/
private void determineProxy( String jiraUrl, HttpClient client )
{
// see whether there is any proxy defined in maven
getProxyInfo( jiraUrl );
if ( proxyHost != null )
{
client.getHostConfiguration().setProxy( proxyHost, proxyPort );
getLog().debug( "Using proxy: " + proxyHost + " at port " + proxyPort );
if ( proxyUser != null )
{
getLog().debug( "Using proxy user: " + proxyUser );
client.getState().setProxyCredentials(
new AuthScope( null, AuthScope.ANY_PORT, null,
AuthScope.ANY_SCHEME ),
new UsernamePasswordCredentials( proxyUser, proxyPass ) );
}
}
}
/**
* Downloads the given link using the configured HttpClient, possibly following redirects.
*
* @param cl the HttpClient
* @param link the URL to JIRA
*/
private void download( final HttpClient cl, final String link )
{
try
{
GetMethod gm = new GetMethod( link );
getLog().info( "Downloading from JIRA at: " + link );
gm.setFollowRedirects( true );
cl.executeMethod( gm );
StatusLine sl = gm.getStatusLine();
if ( sl == null )
{
getLog().error( "Unknown error validating link: " + link );
return;
}
// if we get a redirect, do so
if ( gm.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY )
{
Header locationHeader = gm.getResponseHeader( "Location" );
if ( locationHeader == null )
{
getLog().warn( "Site sent redirect, but did not set Location header" );
}
else
{
String newLink = locationHeader.getValue();
getLog().debug( "Following redirect to " + newLink );
download( cl, newLink );
}
}
if ( gm.getStatusCode() == HttpStatus.SC_OK )
{
final InputStream responseBodyStream = gm.getResponseBodyAsStream();
if ( !output.getParentFile().exists() )
{
output.getParentFile().mkdirs();
}
// write the response to file
OutputStream out = null;
try
{
out = new FileOutputStream( output );
IOUtil.copy( responseBodyStream, out );
}
finally
{
IOUtil.close( out );
IOUtil.close( responseBodyStream );
}
getLog().debug( "Downloading from JIRA was successful" );
}
else
{
getLog().warn( "Downloading from JIRA failed. Received: [" + gm.getStatusCode() + "]" );
}
}
catch ( HttpException e )
{
if ( getLog().isDebugEnabled() )
{
getLog().error( "Error downloading issues from JIRA:", e );
}
else
{
getLog().error( "Error downloading issues from JIRA url: " + e.getLocalizedMessage() );
}
}
catch ( IOException e )
{
if ( getLog().isDebugEnabled() )
{
getLog().error( "Error downloading issues from JIRA:", e );
}
else
{
getLog().error( "Error downloading issues from JIRA. Cause is " + e.getLocalizedMessage() );
}
}
}
public List<Issue> getIssueList()
throws MojoExecutionException
{
if ( output.isFile() )
{
JiraXML jira = new JiraXML( log, jiraDatePattern );
jira.parseXML( output );
getLog().info( "The JIRA version is '" + jira.getJiraVersion() + "'" );
return jira.getIssueList();
}
else
{
getLog().warn( "JIRA file " + output.getPath() + " doesn't exist." );
return Collections.emptyList();
}
}
}