com.microfocus.application.automation.tools.srf.run.RunFromSrfBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.microfocus.application.automation.tools.srf.run.RunFromSrfBuilder.java

Source

/*
 *
 *  Certain versions of software and/or documents (Material?) accessible here may contain branding from
 *  Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.  As of September 1, 2017,
 *  the Material is now offered by Micro Focus, a separately owned and operated company.  Any reference to the HP
 *  and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE
 *  marks are the property of their respective owners.
 * __________________________________________________________________
 * MIT License
 *
 *  Copyright 2012-2018 Micro Focus or one of its affiliates.
 *
 * The only warranties for products and services of Micro Focus and its affiliates
 * and licensors (Micro Focus?) are set forth in the express warranty statements
 * accompanying such products and services. Nothing herein should be construed as
 * constituting an additional warranty. Micro Focus shall not be liable for technical
 * or editorial errors or omissions contained herein.
 * The information contained herein is subject to change without notice.
 * ___________________________________________________________________
 *
 */

package com.microfocus.application.automation.tools.srf.run;

import com.microfocus.application.automation.tools.srf.model.*;
import com.microfocus.application.automation.tools.srf.settings.SrfServerSettingsBuilder;
import com.microfocus.application.automation.tools.srf.results.SrfResultFileWriter;
import com.microfocus.application.automation.tools.srf.utilities.SrfClient;
import com.microfocus.application.automation.tools.srf.utilities.SrfTrustManager;
import com.microfocus.application.automation.tools.srf.utilities.SseEventListener;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import groovy.transform.Synchronized;
import hudson.Extension;
import hudson.Launcher;
import hudson.model.*;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.wagon.authorization.AuthorizationException;
import org.glassfish.jersey.media.sse.EventSource;
import org.glassfish.jersey.media.sse.SseFeature;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.Dispatcher;
import org.kohsuke.stapler.QueryParameter;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.net.ssl.*;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

/**
 * Created by shepshel on 20/07/2016.
 */

public class RunFromSrfBuilder extends Builder implements Serializable, Observer {
    private static final long serialVersionUID = 3;
    private transient PrintStream logger;
    private boolean _https;
    private AbstractBuild<?, ?> build;
    private String srfTestId;
    private String srfBuildNumber;
    private String srfTagNames;
    private String srfReleaseNumber;
    private String srfTunnelName;
    private boolean srfCloseTunnel;
    private List<SrfTestParamsModel> srfTestParameters;
    private JSONArray jobIds;
    private SseEventListener sseEventListener;
    private HashSet<String> runningCount;
    private transient EventSource eventSrc;
    private String _ftaasServerAddress;
    private String _app;
    private String _tenant;
    private String _secret;
    private boolean _secretApplied;
    private transient HttpURLConnection _con;
    private static SrfTrustManager _trustMgr = new SrfTrustManager();
    private static SSLSocketFactory _factory;
    private String _token;
    private CompletableFuture<Boolean> srfExecutionFuture;
    private SrfClient srfClient;
    private static final Logger systemLogger = Logger.getLogger(RunFromSrfBuilder.class.getName());

    @DataBoundConstructor
    public RunFromSrfBuilder(String srfTestId, String srfTagNames, String srfReleaseNumber, String srfBuildNumber,
            String srfTunnelName, boolean srfCloseTunnel, List<SrfTestParamsModel> srfTestParameters) {

        this.srfTestId = srfTestId;
        this.srfTagNames = srfTagNames;
        this.srfTestParameters = srfTestParameters;
        this.srfBuildNumber = srfBuildNumber;
        this.srfReleaseNumber = srfReleaseNumber;
        this.srfCloseTunnel = srfCloseTunnel;
        this.srfTunnelName = srfTunnelName;
    }

    class TestRunData implements Serializable {

        private String id; // "932c6c3e-939e-4b17-a04f-1a2951481758",
        private String name; // "Test-Test-Run",
        private String Start; // "2016-07-25T08:27:59.318Z",
        private String duration;
        private String status; // "status" : "success",
        private String testId; // "246fa1a7-7ed2-4203-a4e9-7ce5fbf4f800",

        public TestRunData(JSONObject obj) {
            try {
                id = (String) obj.get("id");
                status = (String) obj.get("status");
                if (id == null) {
                    id = (String) obj.get("message");
                    status = "failed";
                }
                name = (String) obj.get("name");
                duration = obj.get("durationMs").toString();
            } catch (Exception e) {

            }
        }

        public void merge(TestRunData newData) {
            if (newData.name != null)
                this.name = newData.name;
            if (newData.Start != null)
                this.Start = newData.Start;
            if (newData.duration != null)
                this.duration = newData.duration;
            if (newData.status != null)
                this.status = newData.status;
            if (newData.testId != null)
                this.testId = newData.testId;
            if (newData.duration != null)
                this.duration = newData.duration;
        }
    }

    static class OpenThread extends Thread {
        private final EventSource eventSource;

        public OpenThread(EventSource eventSource) {
            this.eventSource = eventSource;
        }

        @Override
        public void run() {
            eventSource.open();
        }
    }

    static EventSource openAsynch(WebTarget target, String auth) {
        target.request(MediaType.APPLICATION_JSON_TYPE).header("Authorization", auth);
        EventSource eventSource = new EventSource(target, false);
        HttpsURLConnection.setDefaultSSLSocketFactory(RunFromSrfBuilder._factory);
        new OpenThread(eventSource).start();
        return eventSource;
    }

    public String getSrfTestId() {
        return srfTestId;
    }

    public String getSrfTunnelName() {
        return srfTunnelName;
    }

    public boolean getSrfCloseTunnel() {
        return srfCloseTunnel;
    }

    public String getSrfBuildNumber() {
        return srfBuildNumber;
    }

    public String getSrfReleaseNumber() {
        return srfReleaseNumber;
    }

    public String getSrfTagNames() {
        return srfTagNames;
    }

    public List<SrfTestParamsModel> getSrfTestParameters() {
        return srfTestParameters;
    }

    public String getRunResultsFileName() {
        return String.format("report%1d.xml", build.number);
    }

    @Synchronized
    @Override
    public RunFromSrfBuilder.DescriptorImpl getDescriptor() {
        return (RunFromSrfBuilder.DescriptorImpl) super.getDescriptor();
    }

    // TODO: REMOVE THIS AND USE ONLY CLIENT
    public static JSONObject getSrfConnectionData(AbstractBuild<?, ?> build, PrintStream logger) {
        try {
            CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
            // Create all-trusting host name verifier
            HostnameVerifier allHostsValid = new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
            String path = build.getProject().getParent().getRootDir().toString();
            path = path.concat(
                    "/com.microfocus.application.automation.tools.srf.settings.SrfServerSettingsBuilder.xml");
            File file = new File(path);
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document document = documentBuilder.parse(file);
            // This also shows how you can consult the global configuration of the builder
            JSONObject connectionData = new JSONObject();

            String credentialsId = document.getElementsByTagName("credentialsId").item(0).getTextContent();
            UsernamePasswordCredentials credentials = CredentialsProvider.findCredentialById(credentialsId,
                    StandardUsernamePasswordCredentials.class, build, URIRequirementBuilder.create().build());

            String app = credentials.getUsername();
            String tenant = app.substring(1, app.indexOf('_'));
            String secret = credentials.getPassword().getPlainText();
            String server = document.getElementsByTagName("srfServerName").item(0).getTextContent();

            // Normalize SRF server URL string if needed
            if (server.substring(server.length() - 1).equals("/")) {
                server = server.substring(0, server.length() - 1);
            }

            boolean https = true;
            if (!server.startsWith("https://")) {
                if (!server.startsWith("http://")) {
                    String tmp = server;
                    server = "https://";
                    server = server.concat(tmp);
                } else
                    https = false;
            }
            URL urlTmp = new URL(server);
            if (urlTmp.getPort() == -1) {
                if (https)
                    server = server.concat(":443");
                else
                    server = server.concat(":80");
            }
            String srfProxy = "";
            String srfTunnel = "";
            try {
                srfProxy = document.getElementsByTagName("srfProxyName").item(0) != null
                        ? document.getElementsByTagName("srfProxyName").item(0).getTextContent().trim()
                        : null;
                srfTunnel = document.getElementsByTagName("srfTunnelPath").item(0) != null
                        ? document.getElementsByTagName("srfTunnelPath").item(0).getTextContent()
                        : null;
            } catch (Exception e) {
                throw e;
            }
            connectionData.put("app", app);
            connectionData.put("tunnel", srfTunnel);
            connectionData.put("secret", secret);
            connectionData.put("server", server);
            connectionData.put("https", (https) ? "True" : "False");
            connectionData.put("proxy", srfProxy);
            connectionData.put("tenant", tenant);
            return connectionData;
        } catch (ParserConfigurationException e) {
            logger.print(e.getMessage());
            logger.print("\n\r");
        } catch (SAXException | IOException e) {
            logger.print(e.getMessage());
        }
        return null;
    }

    @Override
    public void update(Observable o, Object eventType) {
        SrfSseEventNotification srfSseEventNotification = (SrfSseEventNotification) eventType;
        switch (srfSseEventNotification.srfTestRunEvent) {
        case TEST_RUN_END:
            boolean removed = this.runningCount.remove(srfSseEventNotification.testRunId);
            if (!removed) {
                systemLogger.warning(String.format("Received TEST_RUN_END event for non existing run %s",
                        srfSseEventNotification.testRunId));
                return;
            }

            if (runningCount.size() > 0)
                return;
            break;
        default:
            return;
        }

        JSONArray testRes;

        try {
            testRes = srfClient.getTestRuns(jobIds);

            int sz = testRes.size();
            for (int i = 0; i < sz; i++) {
                JSONObject jo = testRes.getJSONObject(i);
                jo.put("tenantid", _tenant);
            }

            SrfResultFileWriter.writeJsonReport(build.getRootDir().getPath(), testRes.toString());
            SrfResultFileWriter.writeXmlReport(build, testRes, _tenant);
            SrfResultFileWriter.writeOctaneResultsUrlFile(testRes, build.getRootDir().getPath(), _tenant,
                    _ftaasServerAddress);

            switch (build.getResult().toString()) {
            case "SUCCESS":
                this.srfExecutionFuture.complete(true);
                return;
            case "ABORTED":
            case "FAILURE":
                this.srfExecutionFuture.complete(false);
                return;
            default:
                systemLogger.warning(
                        String.format("Received undefined build result: %s", build.getResult().toString()));
                this.srfExecutionFuture.complete(false);
                break;
            }

        } catch (Exception e) {
            logger.print(e.getMessage());
            this.srfExecutionFuture.complete(false);
        } finally {
            cleanUp();
        }
    }

    private JSONArray getTestResults(JSONArray tests) throws IOException {
        String url = "";
        JSONArray res = new JSONArray();
        for (int i = 0; i < tests.size(); i++) {
            url = _ftaasServerAddress.concat("/rest/test-manager/test-runs");
            url = url.concat("?access-token=");
            url = url.concat(_token);
            url = url.concat("&");

            String runId = tests.get(i).toString();
            url = url.concat(String.format("id=%1s", runId));
            url = url.concat("&include=resource,script-runs,script-steps");

            URL srvUrl = new URL(url);
            HttpURLConnection con;
            con = (HttpURLConnection) srvUrl.openConnection();
            con.setDoOutput(true);
            con.setRequestProperty("Content-Type", "application/json");
            int rc = con.getResponseCode();
            if (rc == 500) {
                i--;
                continue;
            }
            BufferedReader br = new BufferedReader(new InputStreamReader((con.getInputStream())));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            JSONArray tmp = JSONArray.fromObject(response.toString());
            res.addAll(tmp);
        }
        return res;
    }

    private String applyJobParams(String val) {
        if ((val.length() > 2) && val.startsWith("${") && val.endsWith("}")) {
            String varName = val.substring(2, val.length() - 1);
            val = build.getBuildVariables().get(varName);
            if (val == null) {
                try {
                    val = build.getEnvironment().get(varName);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else if ((val.length() > 2) && val.startsWith("%") && val.endsWith("%")) {
            String varName = val.substring(1, val.length() - 1);
            try {
                val = build.getEnvironment().get(varName);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return val;
    }

    private JSONObject createExecutionReqBody() throws IOException, SrfException {

        JSONObject data = new JSONObject();
        JSONObject testParams = new JSONObject();

        if (srfTestId != null && !srfTestId.isEmpty()) {
            String[] testIds = normalizeParam(applyJobParams(srfTestId));
            data.put("testYac", testIds);
        } else if (srfTagNames != null && !srfTagNames.isEmpty()) {
            String[] tagNames = normalizeParam(srfTagNames);
            data.put("tags", tagNames);
        } else
            throw new SrfException("Both test id and test tags are empty");

        if (srfTunnelName != null && srfTunnelName.length() > 0) {
            data.put("tunnelName", srfTunnelName);
        }

        if (data.size() == 0) {
            throw new IOException("Wrong filter");
        }

        testParams.put("filter", data);
        String buildNumber = applyJobParams(srfBuildNumber);
        String releaseNumber = applyJobParams(srfReleaseNumber);
        if (buildNumber != null && buildNumber.length() > 0) {
            data.put("build", buildNumber);
        }
        if (releaseNumber != null && releaseNumber.length() > 0)
            data.put("release", releaseNumber);

        this.logger.print(String.format("Required build & release: %1s %2s\n\r", buildNumber, releaseNumber));
        HashMap<String, String> paramObj = new HashMap<String, String>();
        int cnt = 0;

        if (srfTestParameters != null && !srfTestParameters.isEmpty()) {
            cnt = srfTestParameters.size();
            if (cnt > 0)
                logger.print("Parameters: \n\r");
            for (int i = 0; i < cnt; i++) {
                String name = srfTestParameters.get(i).getName();
                String val = applyJobParams(srfTestParameters.get(i).getValue());
                paramObj.put(name, val);
                logger.print(String.format("%1s : %2s\n\r", name, val));
            }
        }

        if (cnt > 0)
            data.put("params", paramObj);

        return data;
    }

    private String[] normalizeParam(String paramToNormalize) {
        String[] params = paramToNormalize.split(",");
        for (int i = 0; i < params.length; i++) {
            // Normalize param
            String param = params[i];
            params[i] = param.trim();
        }
        return params;
    }

    private JSONArray executeTestsSet() throws IOException, SrfException, AuthorizationException {
        JSONObject requestBody = createExecutionReqBody();
        JSONArray jobs = srfClient.executeTestsSet(requestBody);
        if (jobs == null || jobs.size() == 0)
            throw new SrfException(String.format("No tests found for %s",
                    this.srfTestId != null && !this.srfTestId.equals("") ? "test id: " + this.srfTestId
                            : "test tags: " + this.srfTagNames));
        return getJobIds(jobs);
    }

    private JSONArray getJobIds(JSONArray jobs) {
        JSONArray jobIds = new JSONArray();
        int cnt = jobs.size();
        for (int k = 0; k < cnt; k++) {
            JSONObject job = jobs.getJSONObject(k);
            try {
                if (job.has("error")) {
                    String errorClassName = job.get("error").getClass().getSimpleName();
                    switch (errorClassName) {
                    case "JSONObject":
                        JSONObject jobExecutionError = job.getJSONObject("error");
                        handleJobError(jobIds, jobExecutionError);
                        break;
                    case "JSONArray":
                        JSONArray jobExecutionErrors = job.getJSONArray("error");
                        for (Object jobError : jobExecutionErrors) {
                            JSONObject error = (JSONObject) jobError;
                            handleJobError(jobIds, error);
                        }
                        break;
                    default:
                        throw new SrfException(String.format(
                                "Received unexpected error class type, expected 'JSONObject' or 'JSONArray' but received %s",
                                errorClassName));
                    }
                } else {
                    jobIds.add(job.getString("jobId"));
                    runningCount.add(job.getString("testRunId"));
                }
            } catch (Exception e) {
                systemLogger.severe(e.getLocalizedMessage());
            }
        }
        return jobIds;
    }

    private void handleJobError(JSONArray jobIds, JSONObject jobExecutionError) {
        JSONObject errorParameters = jobExecutionError.getJSONObject("parameters");
        String testRunId = errorParameters.getString("testRunId");
        // Make sure we won't add the same run in case job has multiple errors
        if (!runningCount.contains(testRunId)) {
            jobIds.add(errorParameters.getString("jobId"));
            runningCount.add(testRunId);
        }
    }

    private String addAuthentication(HttpsURLConnection con) {
        String auth = _app + ":" + _secret;
        byte[] auth64 = Base64.encodeBase64(auth.getBytes());
        String data = "Basic " + new String(auth64);
        if (con != null)
            con.addRequestProperty("Authorization", data);
        return data;
    }

    private void initSrfEventListener()
            throws IOException, IllegalArgumentException, SrfException, AuthorizationException {
        // !!! Important Notice !!!
        // by using 'level=session' in the sse request we're ensuring that we'll get only events related
        // to this run since we're creating a new session (token) for each run
        String urlSSe = _ftaasServerAddress.concat("/rest/test-manager/events").concat(
                "?level=session&types=test-run-started,test-run-ended,test-run-count,script-step-updated,script-step-created,script-run-started,script-run-ended");
        urlSSe = urlSSe.concat("&access-token=").concat(_token);

        ClientBuilder sslBuilder = ClientBuilder.newBuilder();
        SSLContext sslContext;
        try {
            sslContext = SSLContext.getInstance("TLS");
            _trustMgr = new SrfTrustManager();
            sslContext.init(null, new SrfTrustManager[] { _trustMgr }, null);
            SSLContext.setDefault(sslContext);
        } catch (NoSuchAlgorithmException | KeyManagementException e1) {
            return;
        }

        sslBuilder.register(SSLContext.class);
        Client client = sslBuilder.register(SseFeature.class).build();
        client.register(sslContext);
        try {
            client.getSslContext().init(null, new SrfTrustManager[] { _trustMgr }, null);
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        WebTarget target = client.target(urlSSe);

        eventSrc = openAsynch(target, addAuthentication(null));
        if (eventSrc == null) {
            throw new SrfException("Failed to initiate open event source for SRF SSE");
        }

        eventSrc.register(this.sseEventListener);
    }

    @Override
    public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher, BuildListener _listener)
            throws InterruptedException, IOException {

        this.logger = _listener.getLogger();
        Dispatcher.TRACE = true;
        Dispatcher.TRACE_PER_REQUEST = true;

        this._token = null; // Important in order to get only this run events
        this.build = build;
        this.sseEventListener = new SseEventListener(this.logger);
        this.sseEventListener.addObserver(this);
        this.srfExecutionFuture = new CompletableFuture<>();
        this.runningCount = new HashSet<>();

        JSONObject conData = getSrfConnectionData(build, logger);
        if (conData == null)
            return false;

        _app = conData.getString("app");
        _secret = conData.getString("secret");
        _ftaasServerAddress = conData.getString("server");
        _https = conData.getBoolean("https");
        _tenant = conData.getString("tenant");
        String srfProxy = conData.getString("proxy");

        URL proxy = null;
        if ((srfProxy != null) && (srfProxy.length() != 0)) {
            proxy = new URL(srfProxy);
            String proxyHost = proxy.getHost();
            String proxyPort = String.format("%d", proxy.getPort());
            Properties systemProperties = System.getProperties();
            systemProperties.setProperty("https.proxyHost", proxyHost);
            systemProperties.setProperty("http.proxyHost", proxyHost);
            systemProperties.setProperty("https.proxyPort", proxyPort);
            systemProperties.setProperty("http.proxyPort", proxyPort);
        }

        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            _trustMgr = new SrfTrustManager();
            sslContext.init(null, new SrfTrustManager[] { _trustMgr }, null);
            SSLContext.setDefault(sslContext);
            _factory = sslContext.getSocketFactory();
            this.srfClient = new SrfClient(_ftaasServerAddress, _tenant, _factory, proxy);
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            logger.print(e.getMessage());
            logger.print("\n\r");
        }

        jobIds = null;
        try {
            srfClient.login(_app, _secret);
            this._token = srfClient.getAccessToken();

            initSrfEventListener();
            jobIds = executeTestsSet();

        } catch (UnknownHostException | ConnectException | SSLHandshakeException | IllegalArgumentException
                | AuthorizationException | AuthenticationException e) {
            cleanUp();
            logger.println(
                    String.format("ERROR: Failed logging into SRF server: %s %s", this._ftaasServerAddress, e));
            return false;
        } catch (IOException | SrfException e) {
            cleanUp();
            logger.println(String.format("ERROR: Failed executing test, %s", e));
            return false;
        }

        try {
            boolean buildResult = this.srfExecutionFuture.get();
            return buildResult;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            build.setResult(Result.ABORTED);
            // TODO: Optimization instead of testrunid set maintain testrunid map with job info, in order to avoid already finished job cancellation
            if (!jobIds.isEmpty()) {
                for (int i = 0; i < jobIds.size(); i++) {
                    String jobId = jobIds.get(i).toString();
                    try {
                        srfClient.cancelJob(jobId);
                    } catch (SrfException e1) {
                        e1.printStackTrace();
                    }
                }
            }

            return false;
        } finally {
            cleanUp();
        }
    }

    private void cleanUp() {
        if (eventSrc != null) {
            eventSrc.close();
            eventSrc = null;
        }

        if (_con != null) {
            _con.disconnect();
            _con = null;
        }

        if (srfCloseTunnel && CreateTunnelBuilder.Tunnels != null) {
            for (Process p : CreateTunnelBuilder.Tunnels) {
                p.destroy();
            }
            CreateTunnelBuilder.Tunnels.clear();
        }
    }

    @Extension
    // This indicates to Jenkins that this is an implementation of an extension
    // point.
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
        public DescriptorImpl() {
            load();
        }

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

        @Override
        public String getDisplayName() {
            return "Execute SRF tests";
        }

        public SrfServerSettingsBuilder.SrfDescriptorImpl getSrfServerSettingsBuilderDescriptor()
                throws SrfException {
            return Jenkins.getInstance().getDescriptorByType(SrfServerSettingsBuilder.SrfDescriptorImpl.class);
        }

        public FormValidation doCheckSrfTimeout(@QueryParameter String value) {

            if (StringUtils.isEmpty(value)) {
                return FormValidation.ok();
            }

            String val1 = value.trim();

            if (val1.length() > 0 && val1.charAt(0) == '-')
                val1 = val1.substring(1);

            if (!StringUtils.isNumeric(val1) && !val1.isEmpty()) {
                return FormValidation.error("Timeout name must be a number");
            }
            return FormValidation.ok();
        }

        public FormValidation doCheckSrfTagNames(@QueryParameter String value) {
            if (StringUtils.isBlank(value))
                return FormValidation.ok();

            return FormValidation.ok();
        }

    }

}