JiraWebClient.java Source code

Java tutorial

Introduction

Here is the source code for JiraWebClient.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2009 Brock Janiczak and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Brock Janiczak - initial API and implementation
 *     Tasktop Technologies - improvements
 *     Eugene Kuleshov - improvements
 *******************************************************************************/

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.text.html.HTML.Tag;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.PartBase;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.commons.net.HtmlStreamTokenizer;
import org.eclipse.mylyn.commons.net.HtmlStreamTokenizer.Token;
import org.eclipse.mylyn.commons.net.HtmlTag;

import com.atlassian.connector.eclipse.internal.jira.core.JiraFieldType;
import com.atlassian.connector.eclipse.internal.jira.core.model.Attachment;
import com.atlassian.connector.eclipse.internal.jira.core.model.Component;
import com.atlassian.connector.eclipse.internal.jira.core.model.CustomField;
import com.atlassian.connector.eclipse.internal.jira.core.model.JiraIssue;
import com.atlassian.connector.eclipse.internal.jira.core.model.JiraVersion;
import com.atlassian.connector.eclipse.internal.jira.core.model.Version;
import com.atlassian.connector.eclipse.internal.jira.core.model.WebServerInfo;
import com.atlassian.connector.eclipse.internal.jira.core.service.JiraClient;
import com.atlassian.connector.eclipse.internal.jira.core.service.JiraException;
import com.atlassian.connector.eclipse.internal.jira.core.service.JiraRemoteException;
import com.atlassian.connector.eclipse.internal.jira.core.service.JiraRemoteMessageException;
import com.atlassian.connector.eclipse.internal.jira.core.service.web.rss.JiraRssHandler;

/**
 * @author Brock Janiczak
 * @author Steffen Pingel
 * @author Eugene Kuleshov
 */
// TODO look at creation Operation classes to perform each of these actions
// TODO extract field names into constants
public class JiraWebClient {

    private final JiraClient client;

    private final JiraWebSession session;

    public JiraWebClient(JiraClient client, JiraWebSession session) {
        this.client = client;
        this.session = session;
    }

    public void addCommentToIssue(final JiraIssue issue, final String comment, IProgressMonitor monitor)
            throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient client, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder rssUrlBuffer = new StringBuilder(baseUrl);
                rssUrlBuffer.append("/secure/AddComment.jspa"); //$NON-NLS-1$

                PostMethod post = new PostMethod(rssUrlBuffer.toString());
                post.setRequestHeader("Content-Type", getContentType(monitor)); //$NON-NLS-1$
                prepareSecurityToken(post);
                post.addParameter("comment", comment); //$NON-NLS-1$
                post.addParameter("commentLevel", ""); //$NON-NLS-1$ //$NON-NLS-2$
                post.addParameter("id", issue.getId()); //$NON-NLS-1$

                try {
                    execute(post);
                    if (!expectRedirect(post, "/browse/" + issue.getKey(), false)) { //$NON-NLS-1$
                        handleErrorMessage(post);
                    }
                } finally {
                    post.releaseConnection();
                }
            }

        });
    }

    private String getContentType(IProgressMonitor monitor) throws JiraException {
        return "application/x-www-form-urlencoded; charset=" + client.getCharacterEncoding(monitor); //$NON-NLS-1$
    }

    // TODO refactor common parameter configuration with advanceIssueWorkflow() method
    public void updateIssue(final JiraIssue issue, final String comment, IProgressMonitor monitor)
            throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient client, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder rssUrlBuffer = new StringBuilder(baseUrl);
                rssUrlBuffer.append("/secure/EditIssue.jspa"); //$NON-NLS-1$

                PostMethod post = new PostMethod(rssUrlBuffer.toString());
                post.setRequestHeader("Content-Type", getContentType(monitor)); //$NON-NLS-1$
                prepareSecurityToken(post);
                post.addParameter("summary", issue.getSummary()); //$NON-NLS-1$
                post.addParameter("issuetype", issue.getType().getId()); //$NON-NLS-1$
                if (issue.getPriority() != null) {
                    post.addParameter("priority", issue.getPriority().getId()); //$NON-NLS-1$
                }
                addFields(client, issue, post, new String[] { "duedate" }); //$NON-NLS-1$
                post.addParameter("timetracking", Long.toString(issue.getEstimate() / 60) + "m"); //$NON-NLS-1$ //$NON-NLS-2$

                Component[] components = issue.getComponents();
                if (components != null) {
                    if (components.length == 0) {
                        post.addParameter("components", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                    } else {
                        for (Component component : components) {
                            post.addParameter("components", component.getId()); //$NON-NLS-1$
                        }
                    }
                }

                Version[] versions = issue.getReportedVersions();
                if (versions != null) {
                    if (versions.length == 0) {
                        post.addParameter("versions", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                    } else {
                        for (Version version : versions) {
                            post.addParameter("versions", version.getId()); //$NON-NLS-1$
                        }
                    }
                }

                Version[] fixVersions = issue.getFixVersions();
                if (fixVersions != null) {
                    if (fixVersions.length == 0) {
                        post.addParameter("fixVersions", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                    } else {
                        for (Version fixVersion : fixVersions) {
                            post.addParameter("fixVersions", fixVersion.getId()); //$NON-NLS-1$
                        }
                    }
                }

                // TODO need to be able to choose unassigned and automatic
                if (issue.getAssignee() != null) {
                    post.addParameter("assignee", issue.getAssignee()); //$NON-NLS-1$
                } else {
                    post.addParameter("assignee", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                }
                if (issue.getReporter() != null) {
                    post.addParameter("reporter", issue.getReporter()); //$NON-NLS-1$
                }
                if (issue.getEnvironment() != null) {
                    post.addParameter("environment", issue.getEnvironment()); //$NON-NLS-1$
                }
                post.addParameter("description", issue.getDescription()); //$NON-NLS-1$

                if (comment != null) {
                    post.addParameter("comment", comment); //$NON-NLS-1$
                }
                post.addParameter("commentLevel", ""); //$NON-NLS-1$ //$NON-NLS-2$
                post.addParameter("id", issue.getId()); //$NON-NLS-1$

                if (issue.getSecurityLevel() != null) {
                    post.addParameter("security", issue.getSecurityLevel().getId()); //$NON-NLS-1$
                }

                addCustomFields(client, issue, post);

                try {
                    execute(post);
                    if (!expectRedirect(post, issue)) {
                        handleErrorMessage(post);
                    }
                } finally {
                    post.releaseConnection();
                }
            }

        });
    }

    public void assignIssueTo(final JiraIssue issue, final int assigneeType, final String user,
            final String comment, IProgressMonitor monitor) throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder rssUrlBuffer = new StringBuilder(baseUrl);
                rssUrlBuffer.append("/secure/AssignIssue.jspa"); //$NON-NLS-1$

                PostMethod post = new PostMethod(rssUrlBuffer.toString());
                post.setRequestHeader("Content-Type", getContentType(monitor)); //$NON-NLS-1$
                prepareSecurityToken(post);

                post.addParameter("assignee", client.getAssigneeParam(issue, assigneeType, user)); //$NON-NLS-1$

                if (comment != null) {
                    post.addParameter("comment", comment); //$NON-NLS-1$
                }
                post.addParameter("commentLevel", ""); //$NON-NLS-1$ //$NON-NLS-2$
                post.addParameter("id", issue.getId()); //$NON-NLS-1$

                try {
                    execute(post);
                    if (!expectRedirect(post, issue)) {
                        handleErrorMessage(post);
                    }
                } finally {
                    post.releaseConnection();
                }
            }

        });
    }

    public void advanceIssueWorkflow(final JiraIssue issue, final String actionKey, final String comment,
            final String[] fields, IProgressMonitor monitor) throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                PostMethod post = new PostMethod(baseUrl + "/secure/CommentAssignIssue.jspa"); //$NON-NLS-1$
                post.setRequestHeader("Content-Type", getContentType(monitor)); //$NON-NLS-1$
                prepareSecurityToken(post);

                post.addParameter("id", issue.getId()); //$NON-NLS-1$
                post.addParameter("action", actionKey); //$NON-NLS-1$
                // method.addParameter("assignee", issue.getAssignee());

                if (comment != null) {
                    post.addParameter("comment", comment); //$NON-NLS-1$
                }
                post.addParameter("commentLevel", ""); //$NON-NLS-1$ //$NON-NLS-2$

                addFields(server, issue, post, fields);

                try {
                    execute(post);
                    if (!expectRedirect(post, issue)) {
                        handleErrorMessage(post);
                    }
                } finally {
                    post.releaseConnection();
                }
            }
        });
    }

    public void attachFile(final JiraIssue issue, final String comment, final PartSource partSource,
            final String contentType, IProgressMonitor monitor) throws JiraException {
        attachFile(issue, comment, new FilePart("filename.1", partSource), contentType, monitor); //$NON-NLS-1$
    }

    public void attachFile(final JiraIssue issue, final String comment, final String filename,
            final byte[] contents, final String contentType, IProgressMonitor monitor) throws JiraException {
        attachFile(issue, comment, new FilePart("filename.1", new ByteArrayPartSource(filename, contents)), //$NON-NLS-1$
                contentType, monitor);
    }

    public void attachFile(final JiraIssue issue, final String comment, final String filename, final File file,
            final String contentType, IProgressMonitor monitor) throws JiraException {
        try {
            FilePartSource partSource = new FilePartSource(filename, file);
            attachFile(issue, comment, new FilePart("filename.1", partSource), contentType, monitor); //$NON-NLS-1$
        } catch (FileNotFoundException e) {
            throw new JiraException(e);
        }
    }

    public void attachFile(final JiraIssue issue, final String comment, final FilePart filePart,
            final String contentType, IProgressMonitor monitor) throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder attachFileURLBuffer = new StringBuilder(baseUrl);
                attachFileURLBuffer.append("/secure/AttachFile.jspa"); //$NON-NLS-1$

                PostMethod post = new PostMethod(attachFileURLBuffer.toString());
                prepareSecurityToken(post);

                List<PartBase> parts = new ArrayList<PartBase>();

                StringPart idPart = new StringPart("id", issue.getId()); //$NON-NLS-1$
                StringPart commentLevelPart = new StringPart("commentLevel", ""); //$NON-NLS-1$ //$NON-NLS-2$

                // The transfer encodings have to be removed for some reason
                // There is no need to send the content types for the strings,
                // as they should be in the correct format
                idPart.setTransferEncoding(null);
                idPart.setContentType(null);

                if (comment != null) {
                    StringPart commentPart = new StringPart("comment", comment); //$NON-NLS-1$
                    commentPart.setTransferEncoding(null);
                    commentPart.setContentType(null);
                    commentPart.setCharSet(client.getCharacterEncoding(monitor));
                    parts.add(commentPart);
                }

                commentLevelPart.setTransferEncoding(null);
                commentLevelPart.setContentType(null);

                filePart.setTransferEncoding(null);
                if (contentType != null) {
                    filePart.setContentType(contentType);
                }
                parts.add(filePart);
                parts.add(idPart);
                parts.add(commentLevelPart);

                post.setRequestEntity(
                        new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), post.getParams()));

                try {
                    execute(post);
                    if (!expectRedirect(post, "/secure/ManageAttachments.jspa?id=" + issue.getId())) { //$NON-NLS-1$
                        handleErrorMessage(post);
                    }
                } finally {
                    post.releaseConnection();
                }
            }
        });
    }

    public void retrieveFile(final JiraIssue issue, final Attachment attachment, final OutputStream out,
            IProgressMonitor monitor) throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {

            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder rssUrlBuffer = new StringBuilder(baseUrl);
                rssUrlBuffer.append("/secure/attachment/"); //$NON-NLS-1$
                rssUrlBuffer.append(attachment.getId());
                rssUrlBuffer.append("/"); //$NON-NLS-1$
                try {
                    rssUrlBuffer
                            .append(URLEncoder.encode(attachment.getName(), server.getCharacterEncoding(monitor)));
                } catch (UnsupportedEncodingException e) {
                    throw new JiraException(e);
                }

                GetMethod get = new GetMethod(rssUrlBuffer.toString());
                try {
                    int result = execute(get);
                    if (result != HttpStatus.SC_OK) {
                        handleErrorMessage(get);
                    } else {
                        IOUtils.copy(get.getResponseBodyAsStream(), out);
                    }
                } catch (IOException e) {
                    throw new JiraException(e);
                } finally {
                    get.releaseConnection();
                }
            }

        });
    }

    public String createIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        return createIssue("/secure/CreateIssueDetails.jspa", issue, monitor); //$NON-NLS-1$
    }

    public String createSubTask(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        return createIssue("/secure/CreateSubTaskIssueDetails.jspa", issue, monitor); //$NON-NLS-1$
    }

    private void prepareSecurityToken(HttpMethod method) {
        // this one is required as of JIRA 4.1
        // see http://confluence.atlassian.com/display/JIRA/Form+Token+Handling#FormTokenHandling-Scripting
        method.setRequestHeader("X-Atlassian-Token", "no-check"); //$NON-NLS-1$//$NON-NLS-2$
    }

    // TODO refactor common parameter configuration with advanceIssueWorkflow() method
    private String createIssue(final String url, final JiraIssue issue, IProgressMonitor monitor)
            throws JiraException {
        final String[] issueKey = new String[1];
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient client, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder attachFileURLBuffer = new StringBuilder(baseUrl);
                attachFileURLBuffer.append(url);

                PostMethod post = new PostMethod(attachFileURLBuffer.toString());
                post.setRequestHeader("Content-Type", getContentType(monitor)); //$NON-NLS-1$
                prepareSecurityToken(post);

                post.addParameter("pid", issue.getProject().getId()); //$NON-NLS-1$
                post.addParameter("issuetype", issue.getType().getId()); //$NON-NLS-1$
                post.addParameter("summary", issue.getSummary()); //$NON-NLS-1$
                if (issue.getPriority() != null) {
                    post.addParameter("priority", issue.getPriority().getId()); //$NON-NLS-1$
                }
                addFields(client, issue, post, new String[] { "duedate" }); //$NON-NLS-1$
                post.addParameter("timetracking", Long.toString(issue.getEstimate() / 60) + "m"); //$NON-NLS-1$ //$NON-NLS-2$

                if (issue.getComponents() != null) {
                    for (int i = 0; i < issue.getComponents().length; i++) {
                        post.addParameter("components", issue.getComponents()[i].getId()); //$NON-NLS-1$
                    }
                } else {
                    post.addParameter("components", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                }

                if (issue.getReportedVersions() != null) {
                    for (int i = 0; i < issue.getReportedVersions().length; i++) {
                        post.addParameter("versions", issue.getReportedVersions()[i].getId()); //$NON-NLS-1$
                    }
                } else {
                    post.addParameter("versions", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                }

                if (issue.getFixVersions() != null) {
                    for (int i = 0; i < issue.getFixVersions().length; i++) {
                        post.addParameter("fixVersions", issue.getFixVersions()[i].getId()); //$NON-NLS-1$
                    }
                } else {
                    post.addParameter("fixVersions", "-1"); //$NON-NLS-1$ //$NON-NLS-2$
                }

                if (issue.getAssignee() == null) {
                    post.addParameter("assignee", "-1"); // Default assignee //$NON-NLS-1$ //$NON-NLS-2$
                } else if (issue.getAssignee().length() == 0) {
                    post.addParameter("assignee", ""); // nobody //$NON-NLS-1$ //$NON-NLS-2$
                } else {
                    post.addParameter("assignee", issue.getAssignee()); //$NON-NLS-1$
                }

                post.addParameter("reporter", client.getUserName()); //$NON-NLS-1$

                post.addParameter("environment", issue.getEnvironment() != null ? issue.getEnvironment() : ""); //$NON-NLS-1$ //$NON-NLS-2$
                post.addParameter("description", issue.getDescription() != null ? issue.getDescription() : ""); //$NON-NLS-1$ //$NON-NLS-2$

                if (issue.getParentId() != null) {
                    post.addParameter("parentIssueId", issue.getParentId()); //$NON-NLS-1$
                }

                if (issue.getSecurityLevel() != null) {
                    post.addParameter("security", issue.getSecurityLevel().getId()); //$NON-NLS-1$
                }

                addCustomFields(client, issue, post);

                try {
                    execute(post);
                    if (!expectRedirect(post, "/browse/", false)) { //$NON-NLS-1$
                        handleErrorMessage(post);
                    } else {
                        final Header locationHeader = post.getResponseHeader("location"); //$NON-NLS-1$
                        // parse issue key from issue URL
                        String location = locationHeader.getValue();
                        int i = location.lastIndexOf("/"); //$NON-NLS-1$
                        if (i != -1) {
                            issueKey[0] = location.substring(i + 1);
                        } else {
                            throw new JiraException(
                                    "The server redirected to an unexpected location while creating an issue: " //$NON-NLS-1$
                                            + location);
                        }
                    }
                } finally {
                    post.releaseConnection();
                }
            }
        });
        assert issueKey[0] != null;
        return issueKey[0];
    }

    public void watchIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        watchUnwatchIssue(issue, true, monitor);
    }

    public void unwatchIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        watchUnwatchIssue(issue, false, monitor);
    }

    private void watchUnwatchIssue(final JiraIssue issue, final boolean watch, IProgressMonitor monitor)
            throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder urlBuffer = new StringBuilder(baseUrl);
                String version = client.getCache().getServerInfo(monitor).getVersion();
                if (new JiraVersion(version).compareTo(JiraVersion.JIRA_4_1) >= 0) {
                    urlBuffer.append("/secure/VoteOrWatchIssue.jspa"); //$NON-NLS-1$
                    urlBuffer.append("?id=").append(issue.getId()); //$NON-NLS-1$
                    urlBuffer.append("&watch=").append(watch ? "watch" : "unwatch"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                } else {
                    urlBuffer.append("/browse/").append(issue.getKey()); //$NON-NLS-1$
                    urlBuffer.append("?watch=").append(Boolean.toString(watch)); //$NON-NLS-1$
                }

                HeadMethod head = new HeadMethod(urlBuffer.toString());

                if (new JiraVersion(version).compareTo(JiraVersion.JIRA_4_1) >= 0) {
                    prepareSecurityToken(head);
                }

                try {
                    int result = execute(head);
                    if (result != HttpStatus.SC_OK) {
                        throw new JiraException("Changing watch status failed. Return code: " + result); //$NON-NLS-1$
                    }
                } finally {
                    head.releaseConnection();
                }
            }

        });
    }

    public void voteIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        voteUnvoteIssue(issue, true, monitor);
    }

    public void unvoteIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        voteUnvoteIssue(issue, false, monitor);
    }

    private void voteUnvoteIssue(final JiraIssue issue, final boolean vote, IProgressMonitor monitor)
            throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {

            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder urlBuffer = new StringBuilder(baseUrl);
                String version = client.getCache().getServerInfo(monitor).getVersion();
                if (new JiraVersion(version).compareTo(JiraVersion.JIRA_4_1) >= 0) {
                    urlBuffer.append("/secure/VoteOrWatchIssue.jspa"); //$NON-NLS-1$
                    urlBuffer.append("?id=").append(issue.getId()); //$NON-NLS-1$
                    urlBuffer.append("&vote=").append(vote ? "vote" : "unvote"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                } else {
                    urlBuffer.append("/browse/").append(issue.getKey()); //$NON-NLS-1$
                    urlBuffer.append("?vote=").append(vote ? "vote" : "unvote"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                }

                HeadMethod head = new HeadMethod(urlBuffer.toString());
                if (new JiraVersion(version).compareTo(JiraVersion.JIRA_4_1) >= 0) {
                    prepareSecurityToken(head);
                }

                try {
                    int result = execute(head);
                    if (result != HttpStatus.SC_OK) {
                        throw new JiraException("Changing vote failed. Return code: " + result); //$NON-NLS-1$
                    }
                } finally {
                    head.releaseConnection();
                }
            }

        });
    }

    public void deleteIssue(final JiraIssue issue, IProgressMonitor monitor) throws JiraException {
        doInSession(monitor, new JiraWebSessionCallback() {

            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                StringBuilder urlBuffer = new StringBuilder(baseUrl);
                urlBuffer.append("/secure/DeleteIssue.jspa"); //$NON-NLS-1$
                urlBuffer.append("?id=").append(issue.getId()); //$NON-NLS-1$
                urlBuffer.append("&confirm=true"); //$NON-NLS-1$

                HeadMethod head = new HeadMethod(urlBuffer.toString());
                try {
                    int result = execute(head);
                    if (result != HttpStatus.SC_OK) {
                        throw new JiraException("Deleting issue failed. Return code: " + result); //$NON-NLS-1$
                    }
                } finally {
                    head.releaseConnection();
                }
            }

        });
    }

    public WebServerInfo getWebServerInfo(IProgressMonitor monitor) throws JiraException {
        final WebServerInfo serverInfo = new WebServerInfo();
        serverInfo.getStatistics().mark();
        session.doInSession(new JiraWebSessionCallback() {
            @Override
            public void run(JiraClient server, String baseUrl, IProgressMonitor monitor) throws JiraException {
                serverInfo.setBaseUrl(session.getBaseURL());
                serverInfo.setCharacterEncoding(session.getCharacterEncoding());
                serverInfo.setInsecureRedirect(session.isInsecureRedirect());
            }
        }, monitor);
        serverInfo.getStatistics().record("Login via web took {0}"); //$NON-NLS-1$
        return serverInfo;
    }

    protected void handleErrorMessage(HttpMethodBase method) throws JiraException {
        try {
            String response = method.getResponseBodyAsString();
            // TODO consider logging the error

            if (method.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
                throw new JiraRemoteException("JIRA system error", null); //$NON-NLS-1$
            }

            if (response == null) {
                throw new JiraRemoteMessageException("Error making JIRA request: " + method.getStatusCode(), ""); //$NON-NLS-1$ //$NON-NLS-2$
            }

            StringReader reader = new StringReader(response);
            try {
                StringBuilder msg = new StringBuilder();
                HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer(reader, null);
                for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer
                        .nextToken()) {
                    if (token.getType() == Token.TAG) {
                        HtmlTag tag = (HtmlTag) token.getValue();

                        String classValue = tag.getAttribute("class"); //$NON-NLS-1$
                        if (classValue != null) {
                            if (tag.getTagType() == Tag.DIV) {
                                if (classValue.startsWith("infoBox") || classValue.startsWith("errorArea") //$NON-NLS-1$ //$NON-NLS-2$
                                        || classValue.contains("error")) { //$NON-NLS-1$
                                    throw new JiraRemoteMessageException(getContent(tokenizer, Tag.DIV));
                                }
                            } else if (tag.getTagType() == Tag.SPAN) {
                                if (classValue.startsWith("errMsg")) { //$NON-NLS-1$
                                    msg.append(getContent(tokenizer, Tag.SPAN));
                                }
                            }
                        }
                    }
                }
                if (msg.length() == 0) {
                    throw new JiraRemoteMessageException(response);
                } else {
                    throw new JiraRemoteMessageException(msg.toString());
                }
            } catch (ParseException e) {
                throw new JiraRemoteMessageException("Error parsing JIRA response: " + method.getStatusCode(), ""); //$NON-NLS-1$ //$NON-NLS-2$
            } finally {
                reader.close();
            }
        } catch (IOException e) {
            throw new JiraException(e);
        }
    }

    private String getContent(HtmlStreamTokenizer tokenizer, Tag closingTag) throws IOException, ParseException {
        StringBuilder sb = new StringBuilder();
        int count = 0;
        for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) {
            if (token.getType() == Token.TAG) {
                HtmlTag tag = (HtmlTag) token.getValue();
                if (tag.getTagType() == closingTag) {
                    if (tag.isEndTag()) {
                        if (count == 0) {
                            break;
                        } else {
                            count--;
                        }
                    } else {
                        count++;
                    }
                }
            }

            sb.append(token.toString());
        }
        return sb.toString();
    }

    private void addCustomFields(JiraClient client, JiraIssue issue, PostMethod post) {
        for (CustomField customField : issue.getCustomFields()) {
            addCustomField(client, post, customField);
        }
    }

    private void addCustomField(JiraClient client, PostMethod post, CustomField customField) {
        for (String value : customField.getValues()) {
            String key = customField.getKey();
            if (includeCustomField(key, value)) {
                if (value != null && (JiraFieldType.DATE.getKey().equals(key)
                        || JiraFieldType.DATETIME.getKey().equals(key))) {
                    try {
                        Date date = JiraRssHandler.getDateTimeFormat().parse(value);
                        DateFormat format;
                        if (JiraFieldType.DATE.getKey().equals(key)) {
                            format = client.getLocalConfiguration().getDateFormat();
                        } else {
                            format = client.getLocalConfiguration().getDateTimeFormat();
                        }
                        value = format.format(date);
                    } catch (ParseException e) {
                        // XXX ignore
                    }
                }
                post.addParameter(customField.getId(), value == null ? "" : value); //$NON-NLS-1$
            }
        }
    }

    private void addFields(JiraClient client, JiraIssue issue, PostMethod post, String[] fields) {
        for (String field : fields) {
            if ("duedate".equals(field)) { //$NON-NLS-1$
                if (issue.getDue() != null) {
                    DateFormat format = client.getLocalConfiguration().getDateFormat();
                    post.addParameter("duedate", format.format(issue.getDue())); //$NON-NLS-1$
                } else {
                    post.addParameter("duedate", ""); //$NON-NLS-1$ //$NON-NLS-2$
                }
            } else if (field.startsWith("customfield_")) { //$NON-NLS-1$
                for (CustomField customField : issue.getCustomFields()) {
                    addCustomField(client, post, customField);
                }
            } else {
                String[] values = issue.getFieldValues(field);
                if (values == null) {
                    // method.addParameter(field, "");
                } else {
                    for (String value : values) {
                        post.addParameter(field, value);
                    }
                }
            }
        }
    }

    private boolean includeCustomField(String key, String value) {
        if (key == null) {
            return true;
        }

        if (key.startsWith("com.pyxis.greenhopper.jira:greenhopper-ranking")) { //$NON-NLS-1$
            // if this field is a valid float sent it back if not ignore it (old greenhopper publishes invalid content in this field)
            try {
                Float.parseFloat(value);
                return true;
            } catch (NumberFormatException e) {
                return false;
            }
        }

        return !key.startsWith("com.atlassian.jira.toolkit") && //$NON-NLS-1$
                !key.startsWith("com.atlassian.jira.ext.charting"); //$NON-NLS-1$
    }

    private void doInSession(IProgressMonitor monitor, JiraWebSessionCallback callback) throws JiraException {
        session.doInSession(callback, monitor);
    }

}