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();