org.apache.maven.plugin.jira.AbstractJiraDownloader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-changes-plugin Show documentation
Show all versions of maven-changes-plugin Show documentation
Creates a release history for inclusion into the site and assists in generating an announcement mail.
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.cookie.CookiePolicy;
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.params.HttpClientParams;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.issues.Issue;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.apache.maven.wagon.proxy.ProxyInfo;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Gets relevant issues in RSS from a given JIRA installation.
*
* Based on version 1.1.2 and patch by Dr. Spock (MPJIRA-8).
*
* @author [email protected]
* @author [email protected]
* @version $Id: AbstractJiraDownloader.java 1328930 2012-04-22 18:42:02Z bimargulies $
*/
public abstract class AbstractJiraDownloader
{
private static final String UTF_8 = "UTF-8";
/** Log for debug output. */
protected Log log;
/** Output file for xml document. */
private File output;
/** The maximum number of entries to show. */
private int nbEntriesMax;
/** The filter to apply to query to JIRA. */
private String filter;
/** Ids of fix versions to show, as comma separated string. */
private String fixVersionIds;
/** Ids of status to show, as comma separated string. */
private String statusIds;
/** Ids of resolution to show, as comma separated string. */
private String resolutionIds;
/** Ids of priority to show, as comma separated string. */
private String priorityIds;
/** The component to show. */
private String component;
/** Ids of types to show, as comma separated string. */
private String typeIds;
/** Column names to sort by, as comma separated string. */
private String sortColumnNames;
/** The username to log into JIRA. */
private String jiraUser;
/** The password to log into JIRA. */
private String jiraPassword;
/** The username to log into webserver. */
private String webUser;
/** The password to log into webserver. */
private String webPassword;
/** The maven project. */
private MavenProject project;
/** The maven settings. */
private Settings settings;
/** Mapping containing all allowed JIRA status values. */
protected final Map statusMap = new HashMap( 8 );
/** Mapping containing all allowed JIRA resolution values. */
protected final Map resolutionMap = new HashMap( 8 );
/** Mapping containing all allowed JIRA priority values. */
protected final Map priorityMap = new HashMap( 8 );
/** Mapping containing all allowed JIRA type values. */
protected final Map typeMap = new HashMap( 8 );
/** The pattern used to parse dates from the JIRA xml file. */
protected String jiraDatePattern;
/**
* Creates a filter given the parameters and some defaults.
*
* @return request parameters to be added to URL used for downloading the JIRA issues
*/
private String createFilter()
{
// If the user has defined a filter - use that
if ( ( this.filter != null ) && ( this.filter.length() > 0 ) )
{
return this.filter;
}
StringBuffer localFilter = new StringBuffer( 16 );
// add fix versions
if ( fixVersionIds != null )
{
String[] fixVersions = fixVersionIds.split( "," );
for ( int i = 0; i < fixVersions.length; i++ )
{
if ( fixVersions[i].length() > 0 )
{
localFilter.append( "&fixfor=" ).append( fixVersions[i].trim() );
}
}
}
// get the Status Ids
if ( statusIds != null )
{
String[] stats = statusIds.split( "," );
for ( String stat : stats )
{
stat = stat.trim();
String statusParam = statusMap.get( stat );
if ( statusParam != null )
{
localFilter.append( "&statusIds=" ).append( statusParam );
}
else
{
// if it's numeric we can handle it too.
try
{
Integer.parseInt( stat );
localFilter.append( "&statusIds=" ).append( stat );
}
catch ( NumberFormatException nfe )
{
getLog().error( "maven-changes-plugin: invalid statusId " + stat );
}
}
}
}
// get the Priority Ids
if ( priorityIds != null )
{
String[] prios = priorityIds.split( "," );
for ( String prio : prios )
{
prio = prio.trim();
String priorityParam = priorityMap.get( prio );
if ( priorityParam != null )
{
localFilter.append( "&priorityIds=" ).append( priorityParam );
}
}
}
// get the Resolution Ids
if ( resolutionIds != null )
{
String[] resos = resolutionIds.split( "," );
for ( String reso : resos )
{
reso = reso.trim();
String resoParam = resolutionMap.get( reso );
if ( resoParam != null )
{
localFilter.append( "&resolutionIds=" ).append( resoParam );
}
}
}
// add components
if ( component != null )
{
String[] components = component.split( "," );
for ( String component : components )
{
component = component.trim();
if ( component.length() > 0 )
{
localFilter.append( "&component=" ).append( component );
}
}
}
// get the Type Ids
if ( typeIds != null )
{
String[] types = typeIds.split( "," );
for ( String type : types )
{
String typeParam = typeMap.get( type.trim() );
if ( typeParam != null )
{
localFilter.append( "&type=" ).append( typeParam );
}
}
}
// get the Sort order
int validSortColumnNames = 0;
if ( sortColumnNames != null )
{
String[] sortColumnNamesArray = sortColumnNames.split( "," );
// N.B. Add in reverse order (it's the way JIRA 3 likes it!!)
for ( int i = sortColumnNamesArray.length - 1; i >= 0; i-- )
{
String lowerColumnName = sortColumnNamesArray[i].trim().toLowerCase( Locale.ENGLISH );
boolean descending = false;
String fieldName = null;
if ( lowerColumnName.endsWith( "desc" ) )
{
descending = true;
lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 4 ).trim();
}
else if ( lowerColumnName.endsWith( "asc" ) )
{
descending = false;
lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 3 ).trim();
}
if ( "key".equals( lowerColumnName ) )
{
fieldName = "issuekey";
}
else if ( "summary".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "status".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "resolution".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "assignee".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "reporter".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "type".equals( lowerColumnName ) )
{
fieldName = "issuetype";
}
else if ( "priority".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "version".equals( lowerColumnName ) )
{
fieldName = "versions";
}
else if ( "fix version".equals( lowerColumnName ) )
{
fieldName = "fixVersions";
}
else if ( "component".equals( lowerColumnName ) )
{
fieldName = "components";
}
else if ( "created".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
else if ( "updated".equals( lowerColumnName ) )
{
fieldName = lowerColumnName;
}
if ( fieldName != null )
{
localFilter.append( "&sorter/field=" );
localFilter.append( fieldName );
localFilter.append( "&sorter/order=" );
localFilter.append( descending ? "DESC" : "ASC" );
validSortColumnNames++;
}
else
{
// Error in the configuration
getLog().error(
"maven-changes-plugin: The configured value '" + lowerColumnName
+ "' for sortColumnNames is not correct." );
}
}
}
if ( validSortColumnNames == 0 )
{
// Error in the configuration
getLog().error(
"maven-changes-plugin: None of the configured sortColumnNames '" + sortColumnNames + "' are correct." );
}
return localFilter.toString();
}
/**
* 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 );
Map urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
String jiraUrl = urlMap.get( "url" );
getLog().debug( "JIRA lives at: " + jiraUrl );
String jiraId = urlMap.get( "id" );
determineProxy( jiraUrl, client );
prepareBasicAuthentication( client );
boolean jiraAuthenticationSuccessful = false;
if ( isJiraAuthenticationConfigured() )
{
jiraAuthenticationSuccessful = doJiraAuthentication( client, jiraUrl );
}
if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
|| !isJiraAuthenticationConfigured() )
{
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 )
{
getLog().error( "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 = createFilter();
if ( createdFilter.charAt( 0 ) != '&' )
{
fullURL += "&";
}
fullURL += createdFilter;
fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
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 );
}
}
}
/**
* Override this method if you need to get issues for a specific Fix For.
*
* @return A Fix For id or null
if you don't have that need
*/
protected String getFixFor()
{
return null;
}
/**
* 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 true
if the authentication was successful, otherwise false
*/
private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
{
// log into JIRA if we have to
String loginUrl = null;
StringBuffer loginLink = new StringBuffer( 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;
}
/**
* Check to see if we think that JIRA authentication is needed.
*
* @return true
if jiraUser and jiraPassword are set, otherwise false
*/
private boolean isJiraAuthenticationConfigured()
{
return ( jiraUser != null ) && ( jiraUser.length() > 0 ) && ( jiraPassword != null );
}
/**
* 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 false
if we find an error message in the response body, otherwise true
* @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().indexOf( loginFailureResponse ) == -1;
}
/**
* 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
Proxy proxy = null;
String proxyHost = null;
int proxyPort = 0;
String proxyUser = null;
String proxyPass = null;
if ( project == null )
{
getLog().error( "No project set. No proxy info available." );
return;
}
if ( settings != null )
{
proxy = settings.getActiveProxy();
}
if ( proxy != null )
{
ProxyInfo proxyInfo = new ProxyInfo();
proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
// Get the host out of the JIRA URL
URL url = null;
try
{
url = new URL( jiraUrl );
}
catch( MalformedURLException e )
{
getLog().error( "Invalid JIRA URL: " + jiraUrl + ". " + e.getMessage() );
}
String jiraHost = null;
if ( url != null )
{
jiraHost = url.getHost();
}
// Validation of proxy method copied from org.apache.maven.wagon.proxy.ProxyUtils.
// @todo Can use original when maven-changes-plugin requires a more recent version of Maven
//if ( ProxyUtils.validateNonProxyHosts( proxyInfo, jiraHost ) )
if ( JiraHelper.validateNonProxyHosts( proxyInfo, jiraHost ) )
{
return;
}
proxyHost = settings.getActiveProxy().getHost();
proxyPort = settings.getActiveProxy().getPort();
proxyUser = settings.getActiveProxy().getUsername();
proxyPass = settings.getActiveProxy().getPassword();
getLog().debug( proxyPass );
}
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 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();
}
}
public void setJiraDatePattern( String jiraDatePattern )
{
this.jiraDatePattern = jiraDatePattern;
}
/**
* Set the output file for the log.
*
* @param thisOutput the output file
*/
public void setOutput( File thisOutput )
{
this.output = thisOutput;
}
public File getOutput()
{
return this.output;
}
/**
* Sets the project.
*
* @param thisProject The project to set
*/
public void setMavenProject( Object thisProject )
{
this.project = (MavenProject) thisProject;
}
/**
* Sets the maximum number of Issues to show.
*
* @param nbEntries The maximum number of Issues
*/
public void setNbEntries( final int nbEntries )
{
nbEntriesMax = nbEntries;
}
/**
* Sets the statusIds.
*
* @param thisStatusIds The id(s) of the status to show, as comma separated string
*/
public void setStatusIds( String thisStatusIds )
{
statusIds = thisStatusIds;
}
/**
* Sets the priorityIds.
*
* @param thisPriorityIds The id(s) of the priority to show, as comma separated string
*/
public void setPriorityIds( String thisPriorityIds )
{
priorityIds = thisPriorityIds;
}
/**
* Sets the resolutionIds.
*
* @param thisResolutionIds The id(s) of the resolution to show, as comma separated string
*/
public void setResolutionIds( String thisResolutionIds )
{
resolutionIds = thisResolutionIds;
}
/**
* Sets the sort column names.
*
* @param thisSortColumnNames The column names to sort by
*/
public void setSortColumnNames( String thisSortColumnNames )
{
sortColumnNames = thisSortColumnNames;
}
/**
* Sets the password for authentication against the webserver.
*
* @param thisWebPassword The password of the webserver
*/
public void setWebPassword( String thisWebPassword )
{
this.webPassword = thisWebPassword;
}
/**
* Sets the username for authentication against the webserver.
*
* @param thisWebUser The username of the webserver
*/
public void setWebUser( String thisWebUser )
{
this.webUser = thisWebUser;
}
/**
* Sets the password to log into a secured JIRA.
*
* @param thisJiraPassword The password for JIRA
*/
public void setJiraPassword( final String thisJiraPassword )
{
this.jiraPassword = thisJiraPassword;
}
/**
* Sets the username to log into a secured JIRA.
*
* @param thisJiraUser The username for JIRA
*/
public void setJiraUser( String thisJiraUser )
{
this.jiraUser = thisJiraUser;
}
/**
* Sets the filter to apply to query to JIRA.
*
* @param thisFilter The filter to query JIRA
*/
public void setFilter( String thisFilter )
{
this.filter = thisFilter;
}
/**
* Sets the component(s) to apply to query JIRA.
*
* @param theseComponents The id(s) of components to show, as comma separated string
*/
public void setComponent( String theseComponents )
{
this.component = theseComponents;
}
/**
* Sets the fix version id(s) to apply to query JIRA.
*
* @param theseFixVersionIds The id(s) of fix versions to show, as comma separated string
*/
public void setFixVersionIds( String theseFixVersionIds )
{
this.fixVersionIds = theseFixVersionIds;
}
/**
* Sets the typeIds.
*
* @param theseTypeIds The id(s) of the types to show, as comma separated string
*/
public void setTypeIds( String theseTypeIds )
{
typeIds = theseTypeIds;
}
public void setLog( Log log )
{
this.log = log;
}
private Log getLog()
{
return log;
}
public void setSettings( Settings settings )
{
this.settings = settings;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy