com.blackducksoftware.integration.hub.jenkins.PostBuildScanDescriptor.java Source code

Java tutorial

Introduction

Here is the source code for com.blackducksoftware.integration.hub.jenkins.PostBuildScanDescriptor.java

Source

/*******************************************************************************
 * Copyright (C) 2016 Black Duck Software, Inc.
 * http://www.blackducksoftware.com/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2 only
 * as published by the Free Software Foundation.
 *
 * 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 version 2
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *******************************************************************************/
package com.blackducksoftware.integration.hub.jenkins;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.List;

import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebMethod;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.blackducksoftware.integration.hub.HubIntRestService;
import com.blackducksoftware.integration.hub.exception.BDRestException;
import com.blackducksoftware.integration.hub.jenkins.exceptions.BDJenkinsHubPluginException;
import com.blackducksoftware.integration.hub.jenkins.helper.BuildHelper;
import com.blackducksoftware.integration.hub.jenkins.helper.PluginHelper;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import com.cloudbees.plugins.credentials.matchers.IdMatcher;

import hudson.Extension;
import hudson.ProxyConfiguration;
import hudson.model.AbstractProject;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Descriptor;
import hudson.security.ACL;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import hudson.util.FormValidation;
import hudson.util.FormValidation.Kind;
import hudson.util.IOUtils;
import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

//This indicates to Jenkins that this is an implementation of an extension
//point. The ordinal implies an order to the UI element. The Post-Build Actions add new actions in descending order
// so have this ordinal as a higher value than the failure condition Post-Build Action
@Extension(ordinal = 2)
public class PostBuildScanDescriptor extends BuildStepDescriptor<Publisher> implements Serializable {

    private static final String FORM_SERVER_URL = "hubServerUrl";

    private static final String FORM_TIMEOUT = "hubTimeout";

    private static final String FORM_CREDENTIALSID = "hubCredentialsId";

    private HubServerInfo hubServerInfo;

    /**
     * In order to load the persisted global configuration, you have to call
     * load() in the constructor.
     */
    public PostBuildScanDescriptor() {
        super(PostBuildHubScan.class);
        load();
        HubServerInfoSingleton.getInstance().setServerInfo(hubServerInfo);
    }

    /**
     * @return the hubServerInfo
     */
    public HubServerInfo getHubServerInfo() {
        return HubServerInfoSingleton.getInstance().getServerInfo();
    }

    public String getPluginVersion() {
        return PluginHelper.getPluginVersion();
    }

    public String getDefaultProjectName() {
        return "${JOB_NAME}";
    }

    public String getDefaultProjectVersion() {
        return "<unnamed>";
    }

    public String getHubServerUrl() {
        return (getHubServerInfo() == null ? ""
                : (getHubServerInfo().getServerUrl() == null ? "" : getHubServerInfo().getServerUrl()));
    }

    /**
     * We return a String here instead of an int or Integer because the UI needs a String to display correctly
     *
     */
    public String getDefaultTimeout() {
        return String.valueOf(HubServerInfo.getDefaultTimeout());
    }

    /**
     * We return a String here instead of an int or Integer because the UI needs a String to display correctly
     *
     */
    public String getHubTimeout() {
        return getHubServerInfo() == null ? getDefaultTimeout() : String.valueOf(getHubServerInfo().getTimeout());
    }

    public String getHubCredentialsId() {
        return (getHubServerInfo() == null ? ""
                : (getHubServerInfo().getCredentialsId() == null ? "" : getHubServerInfo().getCredentialsId()));
    }

    /**
     * Code from https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/AbstractItem.java#L602
     *
     */
    // This global configuration can now be accessed at {jenkinsUrl}/descriptorByName/{package}.{ClassName}/config.xml
    // EX:
    // http://localhost:8080/descriptorByName/com.blackducksoftware.integration.hub.jenkins.PostBuildScanDescriptor/config.xml
    @WebMethod(name = "config.xml")
    public void doConfigDotXml(final StaplerRequest req, final StaplerResponse rsp)
            throws IOException, TransformerException, hudson.model.Descriptor.FormException,
            ParserConfigurationException, SAXException {
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        boolean changed = false;
        try {
            if (PostBuildScanDescriptor.class.getClassLoader() != originalClassLoader) {
                changed = true;
                Thread.currentThread().setContextClassLoader(PostBuildScanDescriptor.class.getClassLoader());
            }
            if (req.getMethod().equals("GET")) {
                // read
                rsp.setContentType("application/xml");
                IOUtils.copy(getConfigFile().getFile(), rsp.getOutputStream());
                return;
            }
            if (req.getMethod().equals("POST")) {
                // submission
                updateByXml(new StreamSource(req.getReader()));
                return;
            }
            // huh?
            rsp.sendError(javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST);
        } finally {
            if (changed) {
                Thread.currentThread().setContextClassLoader(originalClassLoader);
            }
        }
    }

    public void updateByXml(final Source source)
            throws IOException, TransformerException, ParserConfigurationException, SAXException {
        final TransformerFactory tFactory = TransformerFactory.newInstance();
        final Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        final ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();

        final StreamResult result = new StreamResult(byteOutput);
        transformer.transform(source, result);

        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        final DocumentBuilder builder = factory.newDocumentBuilder();
        final InputSource is = new InputSource(new StringReader(byteOutput.toString("UTF-8")));
        final Document doc = builder.parse(is);

        final HubServerInfo serverInfo = new HubServerInfo();

        if (doc.getElementsByTagName("hubServerInfo").getLength() > 0) {
            final Node hubServerInfoNode = doc.getElementsByTagName("hubServerInfo").item(0);
            if (hubServerInfoNode != null && hubServerInfoNode.getNodeType() == Node.ELEMENT_NODE) {
                final Element hubServerInfoElement = (Element) hubServerInfoNode;

                final Node credentialsNode = hubServerInfoElement.getElementsByTagName("credentialsId").item(0);
                String credentialId = "";
                if (credentialsNode != null && credentialsNode.getChildNodes() != null
                        && credentialsNode.getChildNodes().item(0) != null) {
                    credentialId = credentialsNode.getChildNodes().item(0).getNodeValue();
                    if (credentialId != null) {
                        credentialId = credentialId.trim();
                    }
                }

                final Node serverUrlNode = hubServerInfoElement.getElementsByTagName("serverUrl").item(0);
                String serverUrl = "";
                if (serverUrlNode != null && serverUrlNode.getChildNodes() != null
                        && serverUrlNode.getChildNodes().item(0) != null) {
                    serverUrl = serverUrlNode.getChildNodes().item(0).getNodeValue();
                    if (serverUrl != null) {
                        serverUrl = serverUrl.trim();
                    }
                }
                final Node timeoutNode = hubServerInfoElement.getElementsByTagName("hubTimeout").item(0);
                String hubTimeout = String.valueOf(HubServerInfo.getDefaultTimeout()); // default timeout
                if (timeoutNode != null && timeoutNode.getChildNodes() != null
                        && timeoutNode.getChildNodes().item(0) != null) {
                    hubTimeout = timeoutNode.getChildNodes().item(0).getNodeValue();
                    if (hubTimeout != null) {
                        hubTimeout = hubTimeout.trim();
                    }
                }

                serverInfo.setCredentialsId(credentialId);
                serverInfo.setServerUrl(serverUrl);

                int serverTimeout = 300;
                try {
                    serverTimeout = Integer.valueOf(hubTimeout);
                } catch (final NumberFormatException e) {
                    System.err.println(
                            "Could not convert the provided timeout : " + hubTimeout + ", to an int value.");
                    e.printStackTrace(System.err);
                }
                serverInfo.setTimeout(serverTimeout);
            }
        }
        hubServerInfo = serverInfo;

        save();
        HubServerInfoSingleton.getInstance().setServerInfo(hubServerInfo);
    }

    @Override
    public boolean isApplicable(final Class<? extends AbstractProject> aClass) {
        return true;
    }

    /**
     * This human readable name is used in the configuration screen.
     */
    @Override
    public String getDisplayName() {
        return Messages.HubBuildScan_getDisplayName();
    }

    @Override
    public boolean configure(final StaplerRequest req, final JSONObject formData) throws Descriptor.FormException {
        // To persist global configuration information,
        // set that to properties and call save().
        final String hubServerUrl = formData.getString(FORM_SERVER_URL);

        hubServerInfo = new HubServerInfo(hubServerUrl, formData.getString(FORM_CREDENTIALSID),
                formData.getInt(FORM_TIMEOUT));
        save();
        HubServerInfoSingleton.getInstance().setServerInfo(hubServerInfo);

        return super.configure(req, formData);
    }

    public FormValidation doCheckScanMemory(@QueryParameter("scanMemory") final String scanMemory)
            throws IOException, ServletException {
        if (StringUtils.isBlank(scanMemory)) {
            return FormValidation.error(Messages.HubBuildScan_getNeedMemory());
        }

        try {
            final Integer scanMem = Integer.valueOf(scanMemory);
            if (scanMem < 256) {
                return FormValidation.error(Messages.HubBuildScan_getInvalidMemoryString());
            }
        } catch (final NumberFormatException e) {
            return FormValidation.error(e, Messages.HubBuildScan_getInvalidMemoryString());
        }

        return FormValidation.ok();
    }

    public FormValidation doCheckBomUpdateMaxiumWaitTime(
            @QueryParameter("bomUpdateMaxiumWaitTime") final String bomUpdateMaxiumWaitTime)
            throws IOException, ServletException {
        if (StringUtils.isBlank(bomUpdateMaxiumWaitTime)) {
            return FormValidation.error(Messages.HubBuildScan_getBomUpdateWaitTimeEmpty());
        }

        try {
            final Integer scanMem = Integer.valueOf(bomUpdateMaxiumWaitTime);
            if (scanMem <= 0) {
                return FormValidation.error(Messages.HubBuildScan_getBomUpdateWaitTimeGreaterThanZero());
            }
            if (scanMem < 2) {
                return FormValidation.warning(Messages.HubBuildScan_getBomUpdateWaitTimeShort());
            }
        } catch (final NumberFormatException e) {
            return FormValidation.error(e, Messages.HubBuildScan_getBomUpdateWaitTimeInvalid());
        }

        return FormValidation.ok();
    }

    /**
     * Fills the Credential drop down list in the global config
     *
     * @return
     */
    public ListBoxModel doFillHubCredentialsIdItems() {

        ListBoxModel boxModel = null;
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        boolean changed = false;
        try {
            if (PostBuildScanDescriptor.class.getClassLoader() != originalClassLoader) {
                changed = true;
                Thread.currentThread().setContextClassLoader(PostBuildScanDescriptor.class.getClassLoader());
            }

            // Code copied from
            // https://github.com/jenkinsci/git-plugin/blob/f6d42c4e7edb102d3330af5ca66a7f5809d1a48e/src/main/java/hudson/plugins/git/UserRemoteConfig.java
            final CredentialsMatcher credentialsMatcher = CredentialsMatchers
                    .anyOf(CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class));
            final AbstractProject<?, ?> project = null; // Dont want to limit the search to a particular project for the drop
            // down menu
            boxModel = new StandardListBoxModel().withEmptySelection().withMatching(credentialsMatcher,
                    CredentialsProvider.lookupCredentials(StandardCredentials.class, project, ACL.SYSTEM,
                            Collections.<DomainRequirement>emptyList()));
        } finally {
            if (changed) {
                Thread.currentThread().setContextClassLoader(originalClassLoader);
            }
        }
        return boxModel;
    }

    /**
     * Fills the drop down list of possible Version phases
     *
     */
    public ListBoxModel doFillHubVersionPhaseItems() {
        return BDCommonDescriptorUtil.doFillHubVersionPhaseItems();
    }

    /**
     * Fills the drop down list of possible Version distribution types
     *
     */
    public ListBoxModel doFillHubVersionDistItems() {
        return BDCommonDescriptorUtil.doFillHubVersionDistItems();
    }

    public FormValidation doCheckTimeout(@QueryParameter("hubTimeout") final String hubTimeout)
            throws IOException, ServletException {
        if (StringUtils.isBlank(hubTimeout)) {
            return FormValidation.error(Messages.HubBuildScan_getPleaseSetTimeout());
        }
        Integer i = 0;
        try {
            i = Integer.valueOf(hubTimeout);
        } catch (final NumberFormatException e) {
            return FormValidation.error(Messages.HubBuildScan_getTimeoutMustBeInteger());
        }
        if (i.equals(0)) {
            return FormValidation.error(Messages.HubBuildScan_getTimeoutCantBeZero());
        }
        return FormValidation.ok();
    }

    /**
     * Performs on-the-fly validation of the form field 'serverUrl'.
     *
     */
    public FormValidation doCheckServerUrl(@QueryParameter("serverUrl") final String serverUrl)
            throws IOException, ServletException {
        if (StringUtils.isBlank(serverUrl)) {
            return FormValidation.error(Messages.HubBuildScan_getPleaseSetServerUrl());
        }
        URL url;
        try {
            url = new URL(serverUrl);
            try {
                url.toURI();
            } catch (final URISyntaxException e) {
                return FormValidation.error(e, Messages.HubBuildScan_getNotAValidUrl());
            }
        } catch (final MalformedURLException e) {
            return FormValidation.error(e, Messages.HubBuildScan_getNotAValidUrl());
        }
        try {
            Proxy proxy = null;

            final Jenkins jenkins = Jenkins.getInstance();
            if (jenkins != null) {
                final ProxyConfiguration proxyConfig = jenkins.proxy;
                if (proxyConfig != null) {
                    proxy = ProxyConfiguration.createProxy(url.getHost(), proxyConfig.name, proxyConfig.port,
                            proxyConfig.noProxyHost);

                    if (proxy != null && proxy != Proxy.NO_PROXY) {

                        if (StringUtils.isNotBlank(proxyConfig.getUserName())
                                && StringUtils.isNotBlank(proxyConfig.getPassword())) {
                            Authenticator.setDefault(new Authenticator() {
                                @Override
                                public PasswordAuthentication getPasswordAuthentication() {
                                    return new PasswordAuthentication(proxyConfig.getUserName(),
                                            proxyConfig.getPassword().toCharArray());
                                }
                            });
                        } else {
                            Authenticator.setDefault(null);
                        }
                    }
                }
            }

            URLConnection connection = null;
            if (proxy != null) {
                connection = url.openConnection(proxy);
            } else {
                connection = url.openConnection();
            }

            connection.getContent();
        } catch (final IOException ioe) {
            return FormValidation.error(ioe, Messages.HubBuildScan_getCanNotReachThisServer_0_(serverUrl));
        } catch (final RuntimeException e) {
            return FormValidation.error(e, Messages.HubBuildScan_getNotAValidUrl());
        }
        return FormValidation.ok();
    }

    public AutoCompletionCandidates doAutoCompleteHubProjectName(
            @QueryParameter("value") final String hubProjectName) throws IOException, ServletException {
        return BDCommonDescriptorUtil.doAutoCompleteHubProjectName(getHubServerInfo(), hubProjectName);
    }

    /**
     * Performs on-the-fly validation of the form field 'hubProjectName'. Checks to see if there is already a project in
     * the Hub with this name.
     *
     */
    public FormValidation doCheckHubProjectName(@QueryParameter("hubProjectName") final String hubProjectName,
            @QueryParameter("hubProjectVersion") final String hubProjectVersion)
            throws IOException, ServletException {
        return BDCommonDescriptorUtil.doCheckHubProjectName(getHubServerInfo(), hubProjectName, hubProjectVersion);
    }

    /**
     * Performs on-the-fly validation of the form field 'hubProjectVersion'. Checks to see if there is already a project
     * in the Hub with this name.
     *
     */
    public FormValidation doCheckHubProjectVersion(
            @QueryParameter("hubProjectVersion") final String hubProjectVersion,
            @QueryParameter("hubProjectName") final String hubProjectName) throws IOException, ServletException {
        return BDCommonDescriptorUtil.doCheckHubProjectVersion(getHubServerInfo(), hubProjectVersion,
                hubProjectName);
    }

    /**
     * Validates that the URL, Username, and Password are correct for connecting to the Hub Server.
     *
     *
     */
    public FormValidation doTestConnection(@QueryParameter("hubServerUrl") final String serverUrl,
            @QueryParameter("hubCredentialsId") final String hubCredentialsId,
            @QueryParameter("hubTimeout") final String hubTimeout) {
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        final boolean changed = false;
        try {
            if (StringUtils.isBlank(serverUrl)) {
                return FormValidation.error(Messages.HubBuildScan_getPleaseSetServerUrl());
            }
            if (StringUtils.isBlank(hubCredentialsId)) {
                return FormValidation.error(Messages.HubBuildScan_getCredentialsNotFound());
            }
            final FormValidation urlCheck = doCheckServerUrl(serverUrl);
            if (urlCheck.kind != Kind.OK) {
                return urlCheck;
            }

            String credentialUserName = null;
            String credentialPassword = null;

            UsernamePasswordCredentialsImpl credential = null;
            final AbstractProject<?, ?> project = null;
            final List<StandardUsernamePasswordCredentials> credentials = CredentialsProvider.lookupCredentials(
                    StandardUsernamePasswordCredentials.class, project, ACL.SYSTEM,
                    Collections.<DomainRequirement>emptyList());
            final IdMatcher matcher = new IdMatcher(hubCredentialsId);
            for (final StandardCredentials c : credentials) {
                if (matcher.matches(c) && c instanceof UsernamePasswordCredentialsImpl) {
                    credential = (UsernamePasswordCredentialsImpl) c;
                }
            }
            if (credential == null) {
                return FormValidation.error(Messages.HubBuildScan_getCredentialsNotFound());
            }
            credentialUserName = credential.getUsername();
            credentialPassword = credential.getPassword().getPlainText();

            final HubIntRestService service = BuildHelper.getRestService(serverUrl, null, null,
                    Integer.valueOf(hubTimeout));

            final int responseCode = service.setCookies(credentialUserName, credentialPassword);

            if (responseCode == 200 || responseCode == 204 || responseCode == 202) {
                return FormValidation.ok(Messages.HubBuildScan_getCredentialsValidFor_0_(serverUrl));
            } else if (responseCode == 401) {
                // If User is Not Authorized, 401 error, an exception should be thrown by the ClientResource
                return FormValidation.error(Messages.HubBuildScan_getCredentialsInValidFor_0_(serverUrl));
            } else {
                return FormValidation.error(Messages.HubBuildScan_getErrorConnectingTo_0_(responseCode));
            }
        } catch (final BDRestException e) {
            String message;
            if (e.getCause() != null) {
                message = e.getCause().toString();
                if (message.contains("(407)")) {
                    return FormValidation.error(e, message);
                }
            }
            return FormValidation.error(e, e.getMessage());
        } catch (final Exception e) {
            String message = null;
            if (e instanceof BDJenkinsHubPluginException) {
                message = e.getMessage();
            } else if (e.getCause() != null && e.getCause().getCause() != null) {
                message = e.getCause().getCause().toString();
            } else if (e.getCause() != null) {
                message = e.getCause().toString();
            } else {
                message = e.toString();
            }
            if (message.toLowerCase().contains("service unavailable")) {
                message = Messages.HubBuildScan_getCanNotReachThisServer_0_(serverUrl);
            } else if (message.toLowerCase().contains("precondition failed")) {
                message = message + ", Check your configuration.";
            }
            return FormValidation.error(e, message);
        } finally {
            if (changed) {
                Thread.currentThread().setContextClassLoader(originalClassLoader);
            }
        }

    }

    /**
     * Creates the Hub project AND/OR version
     *
     *
     */
    public FormValidation doCreateHubProject(@QueryParameter("hubProjectName") final String hubProjectName,
            @QueryParameter("hubProjectVersion") final String hubProjectVersion,
            @QueryParameter("hubVersionPhase") final String hubVersionPhase,
            @QueryParameter("hubVersionDist") final String hubVersionDist) {
        save();
        return BDCommonDescriptorUtil.doCreateHubProject(getHubServerInfo(), hubProjectName, hubProjectVersion,
                hubVersionPhase, hubVersionDist);
    }

}