MCHANGES-294: Support REST API for JIRA
o begin the process. Refactor the JiraDownloader class stack to allow for
both REST and RSS. Probably not quite right yet.
git-svn-id: https://svn.apache.org/repos/asf/maven/plugins/trunk@1411970 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 8a28faa..0320d8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -292,6 +292,13 @@
</exclusions>
</dependency>
+ <!-- rest client for jira -->
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>2.7.0</version>
+ </dependency>
+
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
diff --git a/src/main/java/org/apache/maven/plugin/announcement/AnnouncementMojo.java b/src/main/java/org/apache/maven/plugin/announcement/AnnouncementMojo.java
index 40ba295..a5f32c3 100644
--- a/src/main/java/org/apache/maven/plugin/announcement/AnnouncementMojo.java
+++ b/src/main/java/org/apache/maven/plugin/announcement/AnnouncementMojo.java
@@ -38,8 +38,8 @@
import org.apache.maven.plugin.issues.Issue;
import org.apache.maven.plugin.issues.IssueManagementSystem;
import org.apache.maven.plugin.issues.IssueUtils;
+import org.apache.maven.plugin.jira.ClassicJiraDownloader;
import org.apache.maven.plugin.jira.JIRAIssueManagmentSystem;
-import org.apache.maven.plugin.jira.JiraDownloader;
import org.apache.maven.plugin.trac.TracDownloader;
import org.apache.maven.plugin.trac.TracIssueManagmentSystem;
import org.apache.maven.plugins.annotations.Component;
@@ -688,7 +688,7 @@
protected List<Release> getJiraReleases()
throws MojoExecutionException
{
- JiraDownloader jiraDownloader = new JiraDownloader();
+ ClassicJiraDownloader jiraDownloader = new ClassicJiraDownloader();
File jiraXMLFile = jiraXML;
diff --git a/src/main/java/org/apache/maven/plugin/jira/AbstractJiraDownloader.java b/src/main/java/org/apache/maven/plugin/jira/AbstractJiraDownloader.java
index 3d05fc7..36812f3 100644
--- a/src/main/java/org/apache/maven/plugin/jira/AbstractJiraDownloader.java
+++ b/src/main/java/org/apache/maven/plugin/jira/AbstractJiraDownloader.java
@@ -19,46 +19,18 @@
* 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.plugin.issues.IssueUtils;
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.List;
-import java.util.Map;
/**
- * Gets relevant issues in RSS from a given JIRA installation.
- * <p/>
- * Based on version 1.1.2 and patch by Dr. Spock (MPJIRA-8).
+ * Abstract API, more or less, to retrieving issue information from JIRA.
+ * Intended to have subclasses for the old (RSS) and new (REST) ways of doing things.
*
* @author mfranken@xebia.com
* @author jruiz@exist.com
@@ -66,48 +38,50 @@
*/
public abstract class AbstractJiraDownloader
{
- private static final String UTF_8 = "UTF-8";
+ protected static final String UTF_8 = "UTF-8";
/** Log for debug output. */
protected Log log;
/** Output file for xml document. */
- private File output;
+ protected File output;
/** The maximum number of entries to show. */
- private int nbEntriesMax;
+ protected int nbEntriesMax;
/** The filter to apply to query to JIRA. */
- private String filter;
+ protected String filter;
/** Ids of fix versions to show, as comma separated string. */
- private String fixVersionIds;
+ protected String fixVersionIds;
/** Ids of status to show, as comma separated string. */
- private String statusIds;
+ protected String statusIds;
/** Ids of resolution to show, as comma separated string. */
- private String resolutionIds;
+ protected String resolutionIds;
/** Ids of priority to show, as comma separated string. */
- private String priorityIds;
+ protected String priorityIds;
/** The component to show. */
- private String component;
+ protected String component;
/** Ids of types to show, as comma separated string. */
- private String typeIds;
+ protected String typeIds;
/** Column names to sort by, as comma separated string. */
- private String sortColumnNames;
+ protected String sortColumnNames;
/** The username to log into JIRA. */
- private String jiraUser;
+ protected String jiraUser;
/** The password to log into JIRA. */
- private String jiraPassword;
+ protected String jiraPassword;
/** The username to log into webserver. */
- private String webUser;
+ protected String webUser;
/** The password to log into webserver. */
- private String webPassword;
+ protected String webPassword;
/** The maven project. */
- private MavenProject project;
+ protected MavenProject project;
/** The maven settings. */
- private Settings settings;
- /** Use JQL, JIRA query language, instead of URL parameter based queries */
- private boolean useJql;
+ protected Settings settings;
+ /** Use JQL, JIRA query language, instead of URL parameter based queries.
+ * Note that this is down here to make it easier for the mojo to deal with
+ * both new and old flavors. */
+ protected boolean useJql;
/** Filter the JIRA query based on the current version */
- private boolean onlyCurrentVersion;
+ protected boolean onlyCurrentVersion;
/** The versionPrefix to apply to the POM version */
- private String versionPrefix;
+ protected String versionPrefix;
/** The pattern used to parse dates from the JIRA xml file. */
protected String jiraDatePattern;
@@ -116,505 +90,24 @@
*
* @throws Exception on error
*/
- public void doExecute()
- throws Exception
- {
- try
- {
- HttpClient client = new HttpClient();
+ public abstract void doExecute() throws Exception;
- // 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 fullUrl = null;
-
- if ( useJql )
- {
- fullUrl = getJqlQueryURL();
- }
- else
- {
- fullUrl = getParameterBasedQueryURL( client );
- }
- String baseUrl = JiraHelper.getBaseUrl( fullUrl );
-
- getLog().debug( "JIRA lives at: " + baseUrl );
- determineProxy( baseUrl, client );
-
- prepareBasicAuthentication( client );
-
- boolean jiraAuthenticationSuccessful = false;
- if ( isJiraAuthenticationConfigured() )
- {
- jiraAuthenticationSuccessful = doJiraAuthentication( client, baseUrl );
- }
-
- if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
- || !isJiraAuthenticationConfigured() )
- {
- 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;
- }
- }
-
- /**
- * Override this method if you need to get issues for a specific Fix For.
- *
- * @return A Fix For id or <code>null</code> if you don't have that need
- */
- protected String getFixFor()
- {
- if ( onlyCurrentVersion && useJql )
- {
- // Let JIRA do the filtering of the current version instead of the JIRA mojo.
- // This way JIRA returns less issues and we do not run into the "nbEntriesMax" limit that easily.
-
- String version = ( versionPrefix == null ? "" : versionPrefix ) + project.getVersion();
-
- // Remove "-SNAPSHOT" from the end of the version, if it's there
- if ( version != null && version.endsWith( IssueUtils.SNAPSHOT_SUFFIX ) )
- {
- return version.substring( 0, version.length() - IssueUtils.SNAPSHOT_SUFFIX.length() );
- }
- else
- {
- return version;
- }
- }
- else
- {
- 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 <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 = null;
-
- 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;
- }
/**
* Check to see if we think that JIRA authentication is needed.
*
* @return <code>true</code> if jiraUser and jiraPassword are set, otherwise <code>false</code>
*/
- private boolean isJiraAuthenticationConfigured()
+ protected 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 <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().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<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();
- }
- }
+ public abstract List<Issue> getIssueList() throws MojoExecutionException;
public void setJiraDatePattern( String jiraDatePattern )
{
@@ -781,7 +274,7 @@
this.log = log;
}
- private Log getLog()
+ protected Log getLog()
{
return log;
}
diff --git a/src/main/java/org/apache/maven/plugin/jira/ClassicJiraDownloader.java b/src/main/java/org/apache/maven/plugin/jira/ClassicJiraDownloader.java
new file mode 100644
index 0000000..eff6537
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/jira/ClassicJiraDownloader.java
@@ -0,0 +1,563 @@
+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.plugin.issues.IssueUtils;
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.wagon.proxy.ProxyInfo;
+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.MalformedURLException;
+import java.net.URL;
+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 fullUrl = null;
+
+ if ( useJql )
+ {
+ fullUrl = getJqlQueryURL();
+ }
+ else
+ {
+ fullUrl = getParameterBasedQueryURL( client );
+ }
+ String baseUrl = JiraHelper.getBaseUrl( fullUrl );
+
+ getLog().debug( "JIRA lives at: " + baseUrl );
+ determineProxy( baseUrl, client );
+
+ prepareBasicAuthentication( client );
+
+ boolean jiraAuthenticationSuccessful = false;
+ if ( isJiraAuthenticationConfigured() )
+ {
+ jiraAuthenticationSuccessful = doJiraAuthentication( client, baseUrl );
+ }
+
+ if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
+ || !isJiraAuthenticationConfigured() )
+ {
+ 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;
+ }
+ }
+
+ /**
+ * Override this method if you need to get issues for a specific Fix For.
+ *
+ * @return A Fix For id or <code>null</code> if you don't have that need
+ */
+ protected String getFixFor()
+ {
+ if ( onlyCurrentVersion && useJql )
+ {
+ // Let JIRA do the filtering of the current version instead of the JIRA mojo.
+ // This way JIRA returns less issues and we do not run into the "nbEntriesMax" limit that easily.
+
+ String version = ( versionPrefix == null ? "" : versionPrefix ) + project.getVersion();
+
+ // Remove "-SNAPSHOT" from the end of the version, if it's there
+ if ( version != null && version.endsWith( IssueUtils.SNAPSHOT_SUFFIX ) )
+ {
+ return version.substring( 0, version.length() - IssueUtils.SNAPSHOT_SUFFIX.length() );
+ }
+ else
+ {
+ return version;
+ }
+ }
+ else
+ {
+ 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 <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 = null;
+
+ 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().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<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();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/maven/plugin/jira/JiraDownloader.java b/src/main/java/org/apache/maven/plugin/jira/JiraDownloader.java
deleted file mode 100644
index 631cadc..0000000
--- a/src/main/java/org/apache/maven/plugin/jira/JiraDownloader.java
+++ /dev/null
@@ -1,35 +0,0 @@
-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.
- */
-
-/**
- * Gets relevant issues for a JIRA report.
- *
- * @author mfranken@xebia.com
- * @author jruiz@exist.com
- * @version $Id$
- */
-public final class JiraDownloader
- extends AbstractJiraDownloader
-{
- public JiraDownloader()
- {
- }
-}
diff --git a/src/main/java/org/apache/maven/plugin/jira/JiraMojo.java b/src/main/java/org/apache/maven/plugin/jira/JiraMojo.java
index 94d5cca..e9b36d3 100644
--- a/src/main/java/org/apache/maven/plugin/jira/JiraMojo.java
+++ b/src/main/java/org/apache/maven/plugin/jira/JiraMojo.java
@@ -348,7 +348,7 @@
}
else
{
- issueDownloader = new JiraDownloader();
+ issueDownloader = new ClassicJiraDownloader();
}
configureIssueDownloader( issueDownloader );
issueDownloader.doExecute();