com.autentia.mvn.plugin.changes.BugzillaChangesMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.autentia.mvn.plugin.changes.BugzillaChangesMojo.java

Source

/**
 * bugzillaChanges Maven Mojo. Plugin to generate changes.xml form Bugzilla info, when make release with Maven.
 * Copyright (C) 2009 Autentia Real Business Solutions S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.autentia.mvn.plugin.changes;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

/**
 * Goal to generate the changes.xml file from Bugzilla issues.
 * 
 * @goal changes-generate
 */
public class BugzillaChangesMojo extends AbstractMojo {

    // HTTP parameters and options

    private final static String BEGIN_PARAMETERS = "?";

    private final static String PARAMETERS_UNION = "&";

    private final static String PARAMETERS_ASSIGN = "=";

    private final static String LOGIN_PARAMETER_NAME = "Bugzilla_login";

    private final static String PASSWORD_PARAMETER_NAME = "Bugzilla_password";

    private final static String LOGIN_FAILURE = "The username or password you entered is not valid";

    private final static String RESOLUTION_PARAMETER = "resolution";

    private final static String BUGLIST_URL = "buglist.cgi";

    private final static String PRODUCT_PARAMETER = "product";

    private final static String COMPONENT_PARAMETER = "component";

    private final static String BUGID_PARAMETER = "id";

    private final static String SHOWBUG_URL = "show_bug.cgi";

    private final static String TARGET_PARAMETER = "target_milestone";

    // XML objects

    private static final String ELEMENT_TARGET_MILESTONE = "target_milestone";

    private static final String ATTRIBUTE_VERSION1 = "version1";

    private static final String ATTRIBUTE_VERSION2 = "version2";

    private static final String ELEMENT_ASSIGNED_TO = "assigned_to";

    private static final String ELEMENT_TITLE = "title";

    private static final String ELEMENT_RELEASE = "release";

    private static final String ATTRIBUTE_VERSION = "version";

    private static final String ELEMENT_BODY = "body";

    /**
     * The Maven Project.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * Settings XML configuration.
     * 
     * @parameter expression="${settings}"
     * @required
     * @readonly
     */
    private Settings settings;

    /**
     * Bugzilla product name.
     * 
     * @parameter expression="${changesMavenPlugin.productName}"
     * @required
     */
    private String productName;

    /**
     * Bugzilla product compnent name.
     * 
     * @parameter expression="${changesMavenPlugin.componentName}" default-value="${project.artifactId}"
     */
    private String componentName;

    /**
     * Bugzilla user.
     * 
     * @parameter expression="${changesMavenPlugin.bugzillaUser}"
     * @required
     */
    private String bugzillaUser;

    /**
     * Bugzilla password for the user.
     * 
     * @parameter expression="${changesMavenPlugin.bugzillaPassword}"
     * @required
     */
    private String bugzillaPassword;

    /**
     * Sets the resolution(s) that you want to fetch from Bugzilla. Valid resolutions are: <code>FIXED</code>,
     * <code>INVALID</code>, <code>WONTFIX</code>, <code>DUPLICATE</code>, <code>WORKSFORME</code>,
     * <code>MOVED</code> and <code>---</code>. Multiple values can be separated by commas.
     * 
     * @parameter expression="${changesMavenPlugin.resolution}" default-value="FIXED"
     */
    private String resolution;

    /**
     * Bugzilla login page.
     * 
     * @parameter expression="${changesMavenPlugin.loginPage}" default-value="index.cgi"
     */
    private String loginPage;

    /**
     * The path of the <code>changes.xml</code> file that will be generated to maven-changes-plugin. Must be the same path
     * configured in maven-changes-plugin.
     * 
     * @parameter expression="${changesMavenPlugin.xmlPath}" default-value="src/changes/changes.xml"
     */
    private File xmlPath;

    /**
     * Bugzilla login required.
     * 
     * @parameter expression="${changesMavenPlugin.loginRequired}" default-value="true"
     */
    private boolean loginRequired;

    /**
     * Changes report only in parent project.
     * 
     * @parameter expression="${changesMavenPlugin.parentOnly}" default-value="true"
     */
    private boolean parentOnly;

    /**
     * Fits Bugzilla developers email with Maven developers removing from @ to the end.
     * @parameter expression="${changesMavenPlugin.fitDevelopers}" default-value="true"
     */
    private boolean fitDevelopers;

    /**
     * Changes report only for current version
     * 
     * @parameter expression="${changesMavenPlugin.currentVersionOnly}" default-value="true"
     */
    private boolean currentVersionOnly;

    /**
     * Version name suffix that is removed from the Bugzilla Http request.
     * 
     * @parameter expression="${changesMavenPlugin.versionSuffix}" default-value="-SNAPSHOT"
     */
    private String versionSuffix;

    /**
     * HTTP request manager.
     */
    private HttpRequest httpRequest;

    /**
     * Bugzilla URL.
     */
    private String bugzillaUrl;

    /**
     * Version to be reported
     */
    private String versionName = "";

    private boolean isParentProject() {
        return parentOnly && project.getParent() != null;
    }

    private boolean isEmpty(String str) {
        return str == null || "".equals(str.trim());
    }

    private String getVersionNameFromProject() {
        String vname = project.getVersion();
        final int index = vname.indexOf(versionSuffix);
        if (index != -1) {
            // removing version suffix
            vname = vname.substring(0, index);
        }
        return vname;
    }

    private boolean performLogin(HttpClient client) throws MojoExecutionException {
        boolean success = true;
        if (loginRequired) {
            success = login(client);
        }
        return success;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.plugin.AbstractMojo#execute()
     */
    public void execute() throws MojoExecutionException, MojoFailureException {

        this.getLog().debug("Entering.");
        this.getLog().info("Component:" + this.componentName);

        // si el informe el solo para el padre y es un hijo salimos
        // si es un hijo y no est definido el component salimos
        if (isParentProject() || (parentOnly && isEmpty(componentName))) {
            return;
        }

        versionName = getVersionNameFromProject();

        // inicializamos el gestor de peticiones
        this.httpRequest = new HttpRequest(this.getLog());

        // inicializamos la url del Bugzilla
        this.bugzillaUrl = this.project.getIssueManagement().getUrl();

        // preparamos el cliente HTTP para las peticiones
        final HttpClient client = new HttpClient();
        final HttpClientParams clientParams = client.getParams();
        clientParams.setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
        final HttpState state = new HttpState();
        final HostConfiguration hc = new HostConfiguration();
        client.setHostConfiguration(hc);
        client.setState(state);
        this.determineProxy(client);

        if (!performLogin(client)) {
            throw new MojoExecutionException(
                    "The username or password you entered is not valid. Cannot login in Bugzilla: " + bugzillaUrl);
        }

        final String bugsIds = this.getBugList(client);
        final Document bugsDocument = this.getBugsDocument(client, bugsIds);
        builChangesXML(bugsDocument);

        this.getLog().debug("Exiting.");
    }

    /**
     * Builds changes XML document from Bugzilla XML Document
     * 
     * @param bugsDocument
     * @throws MojoExecutionException
     */
    private void builChangesXML(final Document bugsDocument) throws MojoExecutionException {
        FileOutputStream fos = null;
        Transform t;
        try {
            t = new Transform(this.getClass().getClassLoader().getResourceAsStream("bugzilla.xsl"));
        } catch (final TransformerConfigurationException e) {
            this.getLog().error("Internal XSL error.", e);
            throw new MojoExecutionException("Error with internal XSL transformation file.", e);
        }
        ByteArrayOutputStream baos = null;
        try {
            // creamos los directorios
            this.createFilePath();
            // formamos el documento de cambios
            final byte[] changesXml = t.transformar(bugsDocument);

            // comprobamos si ya existe el fichero y estamos con currentVersionOnly=true.
            // por lo que hay que mantener los cambios de versiones anteriores
            if (this.currentVersionOnly && this.xmlPath.exists()) {
                final Document changesDocument = this.getChangesDocument();
                // buscamos la release que se corresponde con la versin actual
                final NodeList nodelist = changesDocument.getElementsByTagName(ELEMENT_RELEASE);
                boolean replaced = false;
                for (int i = 0; i < nodelist.getLength(); i++) {
                    final Element elementRelease = (Element) nodelist.item(i);
                    if (this.versionName.equals(elementRelease.getAttribute(ATTRIBUTE_VERSION))) {
                        // sustituimos el nodo de la release por el que hemos obtenido ahora
                        this.replaceReleaseNode(elementRelease, changesXml);
                        replaced = true;
                        break;
                    }
                }
                if (!replaced) {
                    this.addNewRelease(changesDocument, changesXml);
                }
                this.saveChangesDocument(changesDocument);

            } else {
                // escribimos el documento XML de cambios
                fos = new FileOutputStream(this.xmlPath);
                baos = new ByteArrayOutputStream();
                baos.write(changesXml);
                baos.writeTo(fos);
                baos.flush();
                fos.flush();
            }

        } catch (final IOException e) {
            this.getLog().error("Error creating file " + this.xmlPath, e);
            throw new MojoExecutionException("Error creating file " + this.xmlPath, e);
        } catch (final TransformerException e) {
            this.getLog().error("Error creating file " + this.xmlPath, e);
            throw new MojoExecutionException("Error creating file " + this.xmlPath, e);
        } finally {
            if (baos != null) {
                try {
                    baos.close();
                } catch (final IOException e) {
                    // ignore
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (final IOException e) {
                    // ignore
                }
            }
        }

    }

    /**
     * Saves new changes document
     * 
     * @param changesDocument
     * @throws IOException
     */
    private void saveChangesDocument(final Document changesDocument) throws IOException {
        final OutputFormat outputFormat = new OutputFormat();
        outputFormat.setEncoding(changesDocument.getInputEncoding());
        outputFormat.setIndenting(true);
        outputFormat.setMethod("XML");
        final FileOutputStream fos = new FileOutputStream(this.xmlPath);
        final XMLSerializer serializer = new XMLSerializer(fos, outputFormat);
        serializer.serialize(changesDocument);
        fos.flush();
        fos.close();
    }

    /**
     * Adds the new release element to changes.xml document
     * 
     * @param changesDocument
     * @param changesXml
     * @throws MojoExecutionException
     */
    private void addNewRelease(final Document changesDocument, final byte[] changesXml)
            throws MojoExecutionException {
        try {
            // formamos el documento obtenido
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            final ByteArrayInputStream bais = new ByteArrayInputStream(changesXml);
            final Document docChanges = db.parse(bais);
            // recuperamos el nodo de la release
            // slo debe haber un elemento
            final Node releaseNode = docChanges.getElementsByTagName(ELEMENT_RELEASE).item(0);

            // lo aadimos al elemento body del que ya tenemos
            // solo hay un body
            final Element bodyElement = (Element) changesDocument.getElementsByTagName(ELEMENT_BODY).item(0);
            final Node importNode = changesDocument.importNode(releaseNode, true);
            bodyElement.appendChild(importNode);
        } catch (final ParserConfigurationException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final SAXException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final IOException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        }
    }

    /**
     * Replaces the current release node in the changes.xml document
     * 
     * @param elementRelease
     * @param changesXml
     * @throws MojoExecutionException
     */
    private void replaceReleaseNode(final Element elementRelease, final byte[] changesXml)
            throws MojoExecutionException {

        try {
            // formamos el documento obtenido
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            final ByteArrayInputStream bais = new ByteArrayInputStream(changesXml);
            final Document docChanges = db.parse(bais);
            // recuperamos el nodo de la release
            // slo debe haber un elemento
            final Node releaseNode = docChanges.getElementsByTagName(ELEMENT_RELEASE).item(0);
            final Node importNode = elementRelease.getOwnerDocument().importNode(releaseNode, true);
            elementRelease.getParentNode().replaceChild(importNode, elementRelease);
        } catch (final ParserConfigurationException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final SAXException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final IOException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        }

    }

    /**
     * Gets XML document from changes.xml
     * 
     * @return
     * @throws MojoExecutionException
     */
    private Document getChangesDocument() throws MojoExecutionException {

        try {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            final Document docChanges = db.parse(this.xmlPath);
            return docChanges;
        } catch (final ParserConfigurationException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final SAXException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        } catch (final IOException e) {
            this.getLog().error("Error reading file " + this.xmlPath, e);
            throw new MojoExecutionException("Error reading file " + this.xmlPath, e);
        }
    }

    /**
     * Gets bugs XML document from Bugzilla.
     * 
     * @param client
     * @param bugsIds
     * @return
     * @throws MojoExecutionException
     */
    private Document getBugsDocument(final HttpClient client, final String bugsIds) throws MojoExecutionException {
        final String link = this.bugzillaUrl + SHOWBUG_URL;
        try {
            final byte[] response = this.httpRequest.sendPostRequest(client, link, bugsIds);

            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            db.setEntityResolver(new EntityResolver() {

                public InputSource resolveEntity(final String publicId, final String systemId)
                        throws SAXException, IOException {
                    return new InputSource(
                            this.getClass().getClassLoader().getResourceAsStream("bugzilla3/bugzilla.dtd"));
                }
            });
            final ByteArrayInputStream bais = new ByteArrayInputStream(response);

            final Document docBugzilla = db.parse(bais);
            this.cleanBugzillaDocument(docBugzilla);
            return docBugzilla;
        } catch (final HttpStatusException e) {
            this.getLog().warn("Can not recover bugs in XML", e);
            throw new MojoExecutionException("Can not recover bugs in XML.", e);
        } catch (final IOException e) {
            this.getLog().warn("Can not recover bugs in XML", e);
            throw new MojoExecutionException("Can not recover bugs in XML.", e);
        } catch (final ParserConfigurationException e) {
            this.getLog().warn("Can not parse XML bugs", e);
            throw new MojoExecutionException("Can not parse XML bugs.", e);
        } catch (final SAXException e) {
            this.getLog().warn("Can not build bugs XML document", e);
            throw new MojoExecutionException("Can not build bugs XML document.", e);
        }
    }

    /**
     * Addapts bugzilla XML document for transformations
     * 
     * @param docBugzilla
     */
    private void cleanBugzillaDocument(final Document docBugzilla) {

        // quitamos el DTD
        final Node docType = docBugzilla.getDoctype();
        docBugzilla.removeChild(docType);

        // ponemos el ttulo
        final Element title = docBugzilla.createElement(ELEMENT_TITLE);
        title.appendChild(docBugzilla.createTextNode(this.project.getName()));
        docBugzilla.getDocumentElement().appendChild(title);

        // ponemos los atributos de version para la ordenacin
        final NodeList target_milestones = docBugzilla.getElementsByTagName(ELEMENT_TARGET_MILESTONE);
        for (int i = 0; i < target_milestones.getLength(); i++) {
            final Element target_milestone = (Element) target_milestones.item(i);
            final String version = target_milestone.getTextContent();
            final String[] versions = version.split("\\.");
            // solo tenemos en cuenta las dos primeras
            // (ej. para version="1.23" tenemos la version1="1" y version2="23";
            // y para version="1.23.3" se tiene lo mismo)
            String version1 = "";
            if (versions.length > 0) {
                version1 = versions[0];
            }
            String version2 = "";
            if (versions.length >= 2) {
                version2 = versions[1];
            }
            target_milestone.setAttribute(ATTRIBUTE_VERSION1, version1);
            target_milestone.setAttribute(ATTRIBUTE_VERSION2, version2);
        }

        // si hay que ajustar los desarrolladores lo procesamos
        if (this.fitDevelopers) {
            final NodeList assigned_tos = docBugzilla.getElementsByTagName(ELEMENT_ASSIGNED_TO);
            for (int i = 0; i < assigned_tos.getLength(); i++) {
                final Element assigned_to = (Element) assigned_tos.item(i);
                String developer = assigned_to.getTextContent();
                final int index = developer.indexOf("@");
                if (index != -1) {
                    developer = developer.substring(0, index);
                }
                // quitamos el texto
                final NodeList childs = assigned_to.getChildNodes();
                for (int j = 0; j < childs.getLength(); j++) {
                    final Node child = childs.item(j);
                    assigned_to.removeChild(child);
                    // disminuimos j debido a que tambin se quita del nodelist
                    j--;
                }
                assigned_to.appendChild(docBugzilla.createTextNode(developer));
            }
        }

        // eliminamos los nodos que no son necesarios
        final String[] nodes2Clean = { "creation_ts", "reporter_accessible", "cclist_accessible",
                "classification_id", "classification", "product", "component", "version", "rep_platform", "op_sys",
                "bug_status", "resolution", "priority", "everconfirmed", "estimated_time", "remaining_time",
                "actual_time", "who", "thetext" };
        for (final String node2clean : nodes2Clean) {
            this.removeNodes(docBugzilla.getElementsByTagName(node2clean));
        }
    }

    /**
     * Removes the nodes from document
     * 
     * @param nodeList
     */
    private void removeNodes(final NodeList nodeList) {
        for (int i = 0; i < nodeList.getLength(); i++) {
            final Node node = nodeList.item(i);
            node.getParentNode().removeChild(node);
            // hay que quitarle uno debido a la implementacin del
            // borrado que tambin elimina del nodelist
            i--;
        }
    }

    /**
     * Gets the bug list from Bugzilla and builds the bug Id's string. [id=bug_id&id=bug_id&...]
     * 
     * @param client
     * @return
     * @throws MojoExecutionException
     */
    private String getBugList(final HttpClient client) throws MojoExecutionException {
        try {
            if (this.productName == null) {
                // recuperamos el nombre del proyecto del POM
                this.productName = this.project.getName();
            }
            final String[] resolutions = this.resolution.split(",");
            String resolution = "";
            for (final String element : resolutions) {

                resolution = resolution + PARAMETERS_UNION + RESOLUTION_PARAMETER + PARAMETERS_ASSIGN
                        + URLEncoder.encode(element, "UTF-8");
            }
            String componentURL = "";
            // si es padre no tenemos en cuenta el componente ya que es todo
            if (this.project.getParent() != null) {
                if ((this.componentName != null) && !this.componentName.equals("")) {
                    componentURL = PARAMETERS_UNION + COMPONENT_PARAMETER + PARAMETERS_ASSIGN
                            + URLEncoder.encode(this.componentName, "UTF-8");

                }
            }

            // version
            String version = "";
            if (this.currentVersionOnly) {

                version = PARAMETERS_UNION + TARGET_PARAMETER + PARAMETERS_ASSIGN
                        + URLEncoder.encode(this.versionName, "UTF-8");
            }
            final String buglistURL = this.bugzillaUrl + BUGLIST_URL + BEGIN_PARAMETERS + PRODUCT_PARAMETER
                    + PARAMETERS_ASSIGN + URLEncoder.encode(this.productName, "UTF-8") + componentURL + resolution
                    + version + "&order=target_milestone";

            final String response = new String(this.httpRequest.sendGetRequest(client, buglistURL), "UTF-8");
            return this.buildBugsIdsString(response);
        } catch (final HttpStatusException e) {
            this.getLog().warn("Can not recover bug list", e);
            throw new MojoExecutionException("Can not recover bug list.", e);
        } catch (final IOException e) {
            this.getLog().warn("Can not recover bug list", e);
            throw new MojoExecutionException("Can not recover bug list.", e);
        }
    }

    /**
     * Builds bugs Id's string. [id=bug_id&id=bug_id&...]
     * 
     * @param response
     * @return
     */
    private String buildBugsIdsString(final String response) {
        final Tidy tidy = new Tidy();
        tidy.setXHTML(true);
        tidy.setMakeClean(true);
        tidy.setBreakBeforeBR(true);
        tidy.setTidyMark(false);
        tidy.setQuoteAmpersand(false);
        tidy.setQuoteMarks(false);
        tidy.setQuoteNbsp(false);
        tidy.setRawOut(true);
        tidy.setFixComments(true);
        tidy.setSmartIndent(true);
        tidy.setWraplen(4000);
        tidy.setDocType("omit");
        tidy.setShowWarnings(false);
        tidy.setQuiet(true);
        tidy.setIndentAttributes(false);
        tidy.setIndentContent(false);
        tidy.setSpaces(0);
        tidy.setTabsize(0);
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final ByteArrayInputStream bais = new ByteArrayInputStream(response.getBytes());
        final Document doc = tidy.parseDOM(bais, baos);
        final NodeList forms = doc.getElementsByTagName("form");
        final StringBuffer ids = new StringBuffer();
        Element formElement = null;

        for (int i = 0; i < forms.getLength(); i++) {
            formElement = (Element) forms.item(i);
            final String action = formElement.getAttribute("action");
            if (action.equals(SHOWBUG_URL)) {
                final NodeList inputs = formElement.getElementsByTagName("input");
                for (int j = 0; j < inputs.getLength(); j++) {
                    final Element input = (Element) inputs.item(j);
                    final String name = input.getAttribute("name");
                    if (name.equals("id")) {
                        ids.append(BUGID_PARAMETER);
                        ids.append(PARAMETERS_ASSIGN);
                        ids.append(input.getAttribute("value"));
                        ids.append(PARAMETERS_UNION);
                    }
                }
                ids.append("ctype");
                ids.append(PARAMETERS_ASSIGN);
                ids.append("xml");
                ids.append(PARAMETERS_UNION);
                ids.append("excludefield");
                ids.append(PARAMETERS_ASSIGN);
                ids.append("attachmentdata");
                break;
            }
        }
        return ids.toString();
    }

    /**
     * Performs Bugzilla login.
     * 
     * @param client
     * @return True if login is successfully.
     * @throws MojoExecutionException
     */
    private boolean login(final HttpClient client) throws MojoExecutionException {
        final String link = this.bugzillaUrl + this.loginPage;
        final String parameters = LOGIN_PARAMETER_NAME + PARAMETERS_ASSIGN + this.bugzillaUser + PARAMETERS_UNION
                + PASSWORD_PARAMETER_NAME + PARAMETERS_ASSIGN + this.bugzillaPassword;

        boolean success = false;
        try {
            final String response = new String(this.httpRequest.sendPostRequest(client, link, parameters), "UTF-8");
            success = response.indexOf(LOGIN_FAILURE) == -1;
        } catch (final HttpStatusException e) {
            this.getLog().warn("Can not do login", e);
            throw new MojoExecutionException("Can not do login.", e);
        } catch (final IOException e) {
            this.getLog().warn("Can not do login", e);
            throw new MojoExecutionException("Can not do login.", e);
        }
        return success;
    }

    /**
     * Setup proxy access if we have to from settings.xml file configuration.
     * 
     * @param client the HttpClient
     */
    private void determineProxy(final 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 (this.project == null) {
            this.getLog().error("No project set. No proxy info available.");

            return;
        }

        if (this.settings != null) {
            proxy = this.settings.getActiveProxy();
        }

        if (proxy != null) {
            proxyHost = this.settings.getActiveProxy().getHost();

            proxyPort = this.settings.getActiveProxy().getPort();

            proxyUser = this.settings.getActiveProxy().getUsername();

            proxyPass = this.settings.getActiveProxy().getPassword();

            this.getLog().debug(proxyPass);
        }

        if (proxyHost != null) {
            client.getHostConfiguration().setProxy(proxyHost, proxyPort);

            this.getLog().debug("Using proxy: " + proxyHost + " at port " + proxyPort);

            if (proxyUser != null) {
                this.getLog().debug("Using proxy user: " + proxyUser);

                client.getState().setProxyCredentials(
                        new AuthScope(null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME),
                        new UsernamePasswordCredentials(proxyUser, proxyPass));
            }
        }
    }

    /**
     * Create directories as necessary for changes.xml location
     */
    private void createFilePath() {
        String path = this.xmlPath.getAbsolutePath();
        final int index = path.lastIndexOf(System.getProperty("file.separator"));
        if (index != -1) {
            path = path.substring(0, index);
            final File directory = new File(path);
            directory.mkdirs();
        }
    }

}