blob: a291ae3e30bc906596f848e2114b903f1bf8f972 [file] [log] [blame]
/*
* 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.
*/
package org.apache.maven.plugins.jira;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.plugins.issues.Issue;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
/**
* 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.
*
*/
public void doExecute() {
try {
HttpClient client = new HttpClient();
// MCHANGES-89 Allow circular redirects
HttpClientParams clientParams = client.getParams();
clientParams.setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
clientParams.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); // MCHANGES-237
HttpState state = new HttpState();
HostConfiguration hc = new HostConfiguration();
client.setHostConfiguration(hc);
client.setState(state);
String baseUrl = JiraHelper.getBaseUrl(project.getIssueManagement().getUrl());
getLog().debug("JIRA lives at: " + baseUrl);
// Here we only need the host part of the URL
determineProxy(baseUrl, client);
prepareBasicAuthentication(client);
boolean jiraAuthenticationSuccessful = false;
if (isJiraAuthenticationConfigured()) {
// Here we only need the parts up to and including the host part of the URL
jiraAuthenticationSuccessful = doJiraAuthentication(client, baseUrl);
}
if ((isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful)
|| !isJiraAuthenticationConfigured()) {
String fullUrl;
if (useJql) {
fullUrl = getJqlQueryURL();
} else {
fullUrl = getParameterBasedQueryURL(client);
}
if (log.isDebugEnabled()) {
log.debug("download jira issues from url " + fullUrl);
}
// execute the GET
download(client, fullUrl);
}
} catch (Exception e) {
if (project.getIssueManagement() != null) {
getLog().error("Error accessing " + project.getIssueManagement().getUrl(), e);
} else {
getLog().error("Error accessing mock project issues", e);
}
}
}
private String getJqlQueryURL() {
// JQL is based on project names instead of project ID's
Map<String, String> urlMap =
JiraHelper.getJiraUrlAndProjectName(project.getIssueManagement().getUrl());
String jiraUrl = urlMap.get("url");
String jiraProject = urlMap.get("project");
if (jiraProject == null) {
throw new RuntimeException("The issue management URL in the POM does not include a JIRA project name");
} else {
// create the URL for getting the proper issues from JIRA
String jqlQuery = new JqlQueryBuilder(log)
.project(jiraProject)
.fixVersion(getFixFor())
.fixVersionIds(fixVersionIds)
.statusIds(statusIds)
.priorityIds(priorityIds)
.resolutionIds(resolutionIds)
.components(component)
.typeIds(typeIds)
.sortColumnNames(sortColumnNames)
.build();
return new UrlBuilder(jiraUrl, "sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml")
.addParameter("tempMax", nbEntriesMax)
.addParameter("reset", "true")
.addParameter("jqlQuery", jqlQuery)
.build();
}
}
private String getParameterBasedQueryURL(HttpClient client) {
Map<String, String> urlMap =
JiraHelper.getJiraUrlAndProjectId(project.getIssueManagement().getUrl());
String jiraUrl = urlMap.get("url");
String jiraId = urlMap.get("id");
if (jiraId == null || jiraId.length() == 0) {
log.debug("The JIRA URL " + project.getIssueManagement().getUrl()
+ " doesn't include a pid, trying to extract it from JIRA.");
jiraId = JiraHelper.getPidFromJira(log, project.getIssueManagement().getUrl(), client);
}
if (jiraId == null) {
throw new RuntimeException("The issue management URL in the POM does not include a pid,"
+ " and it was not possible to extract it from the page at that URL.");
} else {
// create the URL for getting the proper issues from JIRA
String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
if (getFixFor() != null) {
fullURL += "&fixfor=" + getFixFor();
}
String createdFilter = new ParameterQueryBuilder(log)
.fixVersionIds(fixVersionIds)
.statusIds(statusIds)
.priorityIds(priorityIds)
.resolutionIds(resolutionIds)
.components(component)
.typeIds(typeIds)
.sortColumnNames(sortColumnNames)
.filter(filter)
.build();
if (createdFilter.charAt(0) != '&') {
fullURL += "&";
}
fullURL += createdFilter;
fullURL += ("&tempMax=" + nbEntriesMax + "&reset=true&decorator=none");
return fullURL;
}
}
/**
* Check and prepare for basic authentication.
*
* @param client The client to prepare
*/
private void prepareBasicAuthentication(HttpClient client) {
if ((webUser != null) && (webUser.length() > 0)) {
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials(webUser, webPassword);
getLog().debug("Using username: " + webUser + " for Basic Authentication.");
client.getState()
.setCredentials(new AuthScope(null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME), defaultcreds);
}
}
/**
* Authenticate against JIRA. This method relies on jiraUser and jiraPassword being set. You can check this by
* calling isJiraAuthenticationConfigured().
*
* @param client the HttpClient
* @param jiraUrl the JIRA installation
* @return <code>true</code> if the authentication was successful, otherwise <code>false</code>
*/
private boolean doJiraAuthentication(HttpClient client, final String jiraUrl) {
// log into JIRA if we have to
String loginUrl;
StringBuilder loginLink = new StringBuilder(jiraUrl);
loginLink.append("/login.jsp?os_destination=/secure/");
try {
loginLink.append("&os_username=").append(URLEncoder.encode(jiraUser, UTF_8));
String password = null;
if (jiraPassword != null) {
password = StringUtils.repeat("*", jiraPassword.length());
}
getLog().debug("Login URL: " + loginLink + "&os_password=" + password);
loginLink.append("&os_password=").append(URLEncoder.encode(jiraPassword, UTF_8));
loginUrl = loginLink.toString();
// execute the login
GetMethod loginGet = new GetMethod(loginUrl);
client.executeMethod(loginGet);
if (loginSucceeded(loginGet)) {
getLog().debug("Successfully logged in into JIRA.");
return true;
} else {
getLog().warn("Was unable to login into JIRA: wrong username and/or password.");
}
} catch (Exception e) {
if (getLog().isDebugEnabled()) {
getLog().error("Error trying to login into JIRA.", e);
} else {
getLog().error("Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage());
}
}
return false;
}
/**
* Evaluate if the login attempt to JIRA was successful or not. We can't use the status code because JIRA returns
* 200 even if the login fails.
*
* @param loginGet The method that was executed
* @return <code>false</code> if we find an error message in the response body, otherwise <code>true</code>
* @todo There must be a nicer way to know whether we were able to login or not
*/
private boolean loginSucceeded(GetMethod loginGet) throws IOException {
final String loginFailureResponse = "your username and password are incorrect";
return !loginGet.getResponseBodyAsString().contains(loginFailureResponse);
}
/**
* Setup proxy access if we have to.
*
* @param client the HttpClient
*/
private void determineProxy(String jiraUrl, HttpClient client) {
// see whether there is any proxy defined in maven
getProxyInfo(jiraUrl);
if (proxyHost != null) {
client.getHostConfiguration().setProxy(proxyHost, proxyPort);
getLog().debug("Using proxy: " + proxyHost + " at port " + proxyPort);
if (proxyUser != null) {
getLog().debug("Using proxy user: " + proxyUser);
client.getState()
.setProxyCredentials(
new AuthScope(null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME),
new UsernamePasswordCredentials(proxyUser, proxyPass));
}
}
}
/**
* Downloads the given link using the configured HttpClient, possibly following redirects.
*
* @param cl the HttpClient
* @param link the URL to JIRA
*/
private void download(final HttpClient cl, final String link) {
InputStream in = null;
OutputStream out = null;
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) {
in = gm.getResponseBodyAsStream();
if (!output.getParentFile().exists()) {
output.getParentFile().mkdirs();
}
// write the response to file
out = new FileOutputStream(output);
IOUtil.copy(in, out);
out.close();
out = null;
in.close();
in = null;
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());
}
} finally {
IOUtil.close(out);
IOUtil.close(in);
}
}
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();
}
}
}