Java tutorial
// (c) Copyright 2013 Hewlett-Packard Development Company, L.P. // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package com.hp.mercury.ci.jenkins.plugins; import com.hp.mercury.ci.jenkins.plugins.oo.core.oo10x.WaitExecutionResult; import com.hp.mercury.ci.jenkins.plugins.oo.entities.ExecutionResult; import com.hp.mercury.ci.jenkins.plugins.oo.entities.ExecutionStatusState; import com.hp.mercury.ci.jenkins.plugins.oo.entities.StepLog; import com.hp.mercury.ci.jenkins.plugins.oo.entities.TriggeredExecutionDetailsVO; import com.hp.mercury.ci.jenkins.plugins.oo.utils.MapValueCriteria; import com.hp.mercury.ci.jenkins.plugins.oo.utils.StringUtils; import com.hp.mercury.ci.jenkins.plugins.oo.core.*; import com.hp.mercury.ci.jenkins.plugins.oo.encryption.TripleDES; import com.hp.mercury.ci.jenkins.plugins.oo.matcher.MatchStrategy; import com.hp.mercury.ci.jenkins.plugins.oo.ssl.FakeSocketFactory; import hudson.*; import hudson.matrix.*; import hudson.model.*; import hudson.util.FormValidation; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.cookie.CookieSpecBase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.params.ConnManagerParams; import org.apache.http.conn.params.ConnPerRoute; import org.apache.http.conn.params.ConnPerRouteBean; import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.cookie.CookieOrigin; import org.apache.http.cookie.CookieSpec; import org.apache.http.cookie.CookieSpecFactory; import org.apache.http.cookie.CookieSpecRegistry; import org.apache.http.cookie.params.CookieSpecPNames; import org.apache.http.impl.client.DefaultHttpClient; import com.hp.mercury.ci.jenkins.plugins.oo.utils.CollectionUtils; import com.hp.mercury.ci.jenkins.plugins.oo.utils.Criteria; import com.hp.mercury.ci.jenkins.plugins.oo.utils.Handler; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; import jenkins.model.Jenkins; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.conn.scheme.Scheme; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.impl.cookie.BestMatchSpec; import org.apache.http.impl.cookie.BestMatchSpecFactory; import org.apache.http.impl.cookie.BrowserCompatSpec; import org.apache.http.impl.cookie.BrowserCompatSpecFactory; import org.apache.http.impl.cookie.IgnoreSpecFactory; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.bind.JavaScriptMethod; import org.springframework.util.Assert; import javax.xml.bind.JAXB; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.cert.CertificateException; import java.util.*; import static hudson.model.Result.*; public class OOBuildStep extends Builder { public static final List<String> RESULTS = CollectionUtils.map(Arrays.asList(UNSTABLE, FAILURE), new Handler<String, Result>() { public String apply(Result node) { return node.toString(); } }); private static byte[] encodedValue; public static List<String> possibleResults() { return RESULTS; } private enum DEV_MODE { } private String selectedFlow; private String ooServer; private List<OOArg> args; private Result desiredResultType; private String valueToCompareWith; private String stepExecutionTimeout; private String runName; private MatchStrategy matchStrategy; private String retVariableToCheck; private String basepath; // private Boolean changeBuildResult; private static String globalKeystore; private static final Log LOG = LogFactory.getLog(OOBuildStep.class); private boolean abortBuildOnRunFailure = true; //we don't store this in the descriptor because it contains authentication data and we don't want it to be serialized to xml... private static DefaultHttpClient httpClient = null; public static DefaultHttpClient getHttpClient() { final DescriptorImpl descriptor = getDescriptorStatically(); if (httpClient == null && descriptor != null) { initializeHttpClient(descriptor); } return httpClient; } private static void initializeHttpClient(DescriptorImpl descriptor) { final int maxConnectionsPerRoute = 100; final int maxConnectionsTotal = 100; ThreadSafeClientConnManager threadSafeClientConnManager = new ThreadSafeClientConnManager(); threadSafeClientConnManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); threadSafeClientConnManager.setMaxTotal(maxConnectionsTotal); httpClient = new DefaultHttpClient(threadSafeClientConnManager); if (descriptor.isIgnoreSsl()) { threadSafeClientConnManager.getSchemeRegistry() .register(new Scheme("https", 443, new FakeSocketFactory())); } else if (descriptor.getKeystorePath() != null) { try { SSLSocketFactory sslSocketFactory = sslSocketFactoryFromCertificateFile( descriptor.getKeystorePath(), decrypt(descriptor.getKeystorePassword()).toCharArray()); sslSocketFactory.setHostnameVerifier(new BrowserCompatHostnameVerifier()); // For less strict rules in dev mode you can try //sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); threadSafeClientConnManager.getSchemeRegistry() .register(new Scheme("https", 443, sslSocketFactory)); } catch (NoSuchAlgorithmException e) { LOG.error("Could not register https scheme: ", e); } catch (KeyManagementException e) { LOG.error("Could not register https scheme: ", e); } catch (KeyStoreException e) { LOG.error("Could not register https scheme: ", e); } catch (UnrecoverableKeyException e) { LOG.error("Could not register https scheme: ", e); } catch (IOException e) { LOG.error("Could not load keystore file: ", e); } catch (CertificateException e) { LOG.error("Could not load keystore file: ", e); } } final HttpParams params = httpClient.getParams(); final int timeoutInSeconds = descriptor.getTimeout() * 1000; HttpConnectionParams.setConnectionTimeout(params, timeoutInSeconds); HttpConnectionParams.setSoTimeout(params, timeoutInSeconds); HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false); for (OOServer s : descriptor.getOoServers(true).values()) { URL url = null; try { url = new URL(s.getUrl()); } catch (MalformedURLException mue) { //can't happen, we pre-validate the URLS during configuration and set active to false if bad. } //check why it doesn't use the credentials provider httpClient.getCredentialsProvider().setCredentials( new AuthScope(url.getHost(), url.getPort(), AuthScope.ANY_REALM, "basic"), new UsernamePasswordCredentials(s.getUsername(), decrypt(s.getPassword()))); } } public static void setEncodedCredentials(String username, String password) { if (username == null || username.length() == 0) { encodedValue = null; } else { String value = username + ":" + decrypt(password); encodedValue = Base64.encodeBase64(value.getBytes(StandardCharsets.UTF_8)); } } public static byte[] getEncodedCredentials() { return encodedValue; } private static SSLSocketFactory sslSocketFactoryFromCertificateFile(String keyStorePath, char[] password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { char[] ksPassword = password; FileInputStream fis = new FileInputStream(keyStorePath); //while similar to singleton patten in API, this provides a NEW instance. KeyStore trustore = KeyStore.getInstance("JKS"); globalKeystore = keyStorePath; trustore.load(fis, ksPassword); final SSLSocketFactory sslSocketFactory = new SSLSocketFactory(trustore); return sslSocketFactory; } @DataBoundConstructor public OOBuildStep(String ooServer, String basepath, String selectedFlow, List<OOArg> args, String retVariableToCheck, int comparisonOrdinal, String valueToCompareWith, String desiredResultType, String stepExecutionTimeout, String runName) throws Descriptor.FormException { //this should never happen.. if (ooServer == null || ooServer.isEmpty() || !OOAccessibilityLayer.getAvailableServers().contains(ooServer)) { throw new Descriptor.FormException( "illegal OO server chosen. must be non null, non empty, " + "pre-configured OO server label.", "ooServer"); } //this should never happen.. if (selectedFlow == null || selectedFlow.isEmpty()) { throw new Descriptor.FormException("illegal flow chosen. must be non null, non empty.", "selectedFlow"); } //if basepath doesn't start with "/library", make it empty if (basepath == null || !StringUtils.unslashifyPrefix(basepath.toLowerCase()).startsWith("library")) { basepath = ""; } this.ooServer = ooServer; this.selectedFlow = selectedFlow; this.args = args; this.basepath = basepath; this.stepExecutionTimeout = stepExecutionTimeout; this.runName = runName; setStepExecutionTimeout(stepExecutionTimeout); if (retVariableToCheck != null && valueToCompareWith != null && comparisonOrdinal != -1 && desiredResultType != null) { this.retVariableToCheck = retVariableToCheck; this.valueToCompareWith = valueToCompareWith; this.matchStrategy = matchStrategy.values()[comparisonOrdinal]; this.desiredResultType = Result.fromString(desiredResultType); } } @SuppressWarnings("unused") //this method contains all "backwards-compatible" persistence logic. //it runs after the object is deserialized from xml. standard xstream and java serialization API. public Object readResolve() { //basepath property was added late, and we use it when listing flows from server. //if we deserialize an older configuration it will not have a basepath property, and basepath will be null. //list will therefore fail. we fix this by making sure that if we have an older configuration, where the value //is null - we give a good default which provides the current behavior - which is listing all flows. if (basepath == null) { basepath = "/Library"; } return this; } public OOServer getOOServer() { return OOAccessibilityLayer.getOOServer(this.ooServer); } public OOFlow getOOFlow() { return new OOFlow(this.selectedFlow); } public List<OOArg> getArgs() { return args; } public Result getDesiredResultType() { return desiredResultType; } public String getValueToCompareWith() { return valueToCompareWith; } public MatchStrategy getMatchStrategy() { return matchStrategy; } public String getRetVariableToCheck() { return retVariableToCheck; } public static DescriptorImpl getDescriptorStatically() { return Jenkins.getInstance().getDescriptorByType(DescriptorImpl.class); } @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Builder> { //for Musca, Lucian-Cristian who wanted an easy way to configure 10 minutes as default stepExecutionTimeout public static final int DEFAULT_TIMEOUT = 600; private Map<String, OOServer> ooServers; private Boolean ignoreSsl; private Integer timeout; private String keystorePassword; private String keystorePath; //used by config.jelly @SuppressWarnings("unused") public boolean isDevModeEnabled() { final String devMode = DEV_MODE.class.getName(); return System.getProperty(devMode, null) != null; } public Integer getTimeout() { //null safe logic for when this class is first loaded and no user config available return timeout == null ? DEFAULT_TIMEOUT : timeout; } public void setTimeout(Integer timeout) { this.timeout = timeout; } public Map<String, OOServer> getOoServers(boolean filterOnlyActive) { if (ooServers == null) { ooServers = new HashMap<String, OOServer>(); } Map<String, OOServer> copy = new HashMap<String, OOServer>(ooServers); if (filterOnlyActive) { CollectionUtils.filter(copy.entrySet(), new MapValueCriteria<String, OOServer>(new Criteria<OOServer>() { public boolean isSuccessful(OOServer tested) { return tested.isActive(); } })); } return copy; } @SuppressWarnings("unused") //used from ui @JavaScriptMethod public Collection<String> listFlowsForServer(String uniqueLabel, String basepath) throws IOException { final OOServer server = ooServers.get(uniqueLabel); if (server == null) { throw new NullPointerException("You must select an OO server before listing its flows."); } final List<OOFlow> flows = server.getFlows(basepath); return CollectionUtils.map(flows, new Handler<String, OOFlow>() { public String apply(OOFlow node) { return node.getId(); } }); } public boolean isIgnoreSsl() { //null safe logic for when this class is first loaded and no user configuration exists return ignoreSsl != null && ignoreSsl; } public void setIgnoreSsl(Boolean ignoreSsl) { this.ignoreSsl = ignoreSsl; } @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return ooServers != null && !ooServers.isEmpty(); } @Override public String getDisplayName() { return "HP Operations Orchestration Flow Execution Build Step"; } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { // setStepExecutionTimeout(json.getInt("stepExecutionTimeout")); setIgnoreSsl(json.getBoolean("ignoreSsl")); setKeystorePassword(json.getString("keystorePassword")); setKeystorePath(json.getString("keystorePath")); final Object ooServersConfigs = json.get("ooServers"); Collection<Exception> serversConfigurationExceptions = null; if (ooServersConfigs == null) { ooServers = Collections.emptyMap(); serversConfigurationExceptions = Collections.emptyList(); } //handle array else if (ooServersConfigs instanceof JSONArray) { final JSONArray ooServerConfigsArray = (JSONArray) ooServersConfigs; final int configsCount = ooServerConfigsArray.size(); ooServers = new HashMap<String, OOServer>(configsCount); serversConfigurationExceptions = new ArrayList<Exception>(configsCount); for (int i = 0; i < configsCount; i++) { JSONObject ooServerConfig = (JSONObject) ooServerConfigsArray.get(i); Exception serverConfigurationException = handleSingleServerConfig(i, ooServerConfig); if (serverConfigurationException != null) { serversConfigurationExceptions.add(serverConfigurationException); } } } //handle single instance else if (ooServersConfigs instanceof JSONObject) { final JSONObject ooServerConfig = (JSONObject) ooServersConfigs; ooServers = new HashMap<String, OOServer>(1); serversConfigurationExceptions = new ArrayList<Exception>(1); Exception serverConfigurationException = handleSingleServerConfig(0, ooServerConfig); if (serverConfigurationException != null) { serversConfigurationExceptions.add(serverConfigurationException); } } save(); initializeHttpClient(this); ((OOAdministrativeMonitor) Jenkins.getInstance() .getAdministrativeMonitor(OOAdministrativeMonitor.MONITOR_ID)) .setUrlConfigurationErrors(serversConfigurationExceptions); return super.configure(req, json); } private FormException handleSingleServerConfig(int i, JSONObject ooServerConfig) throws FormException { FormException ret = null; final String uniqueLabel = ooServerConfig.getString("uniqueLabel"); if (uniqueLabel == null || uniqueLabel.isEmpty()) { ret = new Descriptor.FormException("illegal ooserver label identifier, must not be null or empty.", "uniqueLabel"); } if (ooServers.containsKey(uniqueLabel)) { ret = new FormException("error, name '" + uniqueLabel + "' is already in use.", "uniqueLabel"); } final String inputUrl = ooServerConfig.getString("hostUrl"); //make sure it's a valid URL try { new URL(inputUrl); } catch (MalformedURLException mue) { ret = new FormException("The URL " + inputUrl + " is not a valid URL.", "hostUrl"); } final OOServer s = new OOServer(uniqueLabel, inputUrl, ooServerConfig.getString("username"), encrypt(ooServerConfig.getString("password")), ret == null //the configuration should be activated if there were no exceptions ); // Check that the new new OO server about to add has a unique URL boolean isUniqueURL = true; for (String key : ooServers.keySet()) { if ((inputUrl != null) && (ooServers.get(key) != null) && (inputUrl.equals(ooServers.get(key).getUrl()))) { isUniqueURL = false; break; } } // Only add the OO server in case it points to a unique ULR(host, port or version must be different) if (isUniqueURL) { ooServers.put(s.getUniqueLabel(), s); } return ret; } //data isn't loaded properly from xml config file without this constructor public DescriptorImpl() { super(); load(); initializeHttpClient(this); } @SuppressWarnings("unused") //ajax validation from UI public FormValidation doCheckKeystorePassword(@QueryParameter String value, @QueryParameter String path) { //decrypt doesn't do anything if the value is not encrypted in the first place. // if it's encrypted in the UI because it was loaded from previously saved configuration, we need to decrypt. value = decrypt(value); try { sslSocketFactoryFromCertificateFile(path, value.toCharArray()); } catch (Exception e) { return FormValidation.error(e, "could not load certificate file."); } return FormValidation.ok(); } public String getKeystorePassword() { return keystorePassword; } public void setKeystorePassword(String keystorePassword) { this.keystorePassword = encrypt(keystorePassword); } @SuppressWarnings("unused") //ajax validation from UI public FormValidation doCheckKeystorePath(@QueryParameter String value) { final File file = new File(value); if (!file.exists()) { return FormValidation.error("key store file '" + value + "' not found."); } if (!file.canRead()) { return FormValidation.error("key store file '" + value + "' not readable."); } if (!file.isAbsolute()) { return FormValidation.error("'" + value + "' is not an absolute path."); } if (file.isDirectory()) { return FormValidation.error("'" + value + "' is a directory, not a certificates file."); } return FormValidation.ok(); } public void setKeystorePath(String keystorePath) { this.keystorePath = keystorePath; } /** * @return the plugin's truststore absolute path or null if no additional trustore is defined */ public String getKeystorePath() { return keystorePath == null || keystorePath.isEmpty() ? null : keystorePath; } } @Override public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) { return super.prebuild(build, listener); } public String getBasepath() { return basepath; } public void setBasepath(String basepath) { this.basepath = basepath; } @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { final List<OOArg> argsToUse = expand(getArgs(), (AbstractBuild<MatrixProject, MatrixBuild>) build, listener); Map<String, String> buildVariables = build.getBuildVariables(); Set<String> keySet = buildVariables.keySet(); OOServer selectedOOServer = getOOServer(); String selectedFlowS = this.selectedFlow; for (String key : keySet) { if (key.startsWith("i_")) { argsToUse.add(new OOArg(key.substring(2), buildVariables.get(key))); listener.getLogger().println("adding input " + key.substring(2) + " with value " + buildVariables.get(key) + " from the matrix axis"); } if (key.startsWith("s_")) { selectedOOServer = OOAccessibilityLayer.getOOServer(buildVariables.get(key)); } if (key.startsWith("f_")) { selectedFlowS = basepath + "/" + buildVariables.get(key); } } Assert.isTrue(selectedOOServer != null, "HP Operations Orchestration Server must be selected"); listener.getLogger().println("Connected to central with label: [" + selectedOOServer.getUniqueLabel() + "], URL: [" + selectedOOServer.getUrl() + "]"); URL url = new URL(selectedOOServer.getUrl()); String urlString = StringUtils.slashify(selectedOOServer.getUrl().replace("PAS", "")) + "oo/rest/v1"; final DescriptorImpl descriptor = getDescriptorStatically(); if (selectedOOServer.getUsername() != null && selectedOOServer.getUsername().length() > 0) { listener.getLogger() .println("Authentication will be used, username: " + selectedOOServer.getUsername()); setEncodedCredentials(selectedOOServer.getUsername(), selectedOOServer.getPassword()); } else { setEncodedCredentials(null, null); } // get version from Central Boolean is9x = OOAccessibilityUtils.isOoVersionLowerThen1010(urlString + "/version"); // log version //listener.getLogger().println("Central version: " + version); //listener.getLogger().println("Global Keystore: " + globalKeystore); listener.getLogger().println("Running " + selectedFlowS + " on " + selectedOOServer.getUniqueLabel() + " with parameters " + argsToUse); // legacy invoke of a flow if (is9x) { OORunRequest runRequest = null; OORunResponse ooBuildStepResult = null; try { runRequest = new OORunRequest(selectedOOServer, new OOFlow(selectedFlowS), argsToUse); ooBuildStepResult = OOAccessibilityLayer.run(runRequest); } catch (Exception e) { e.printStackTrace(listener.getLogger()); if (abortBuildOnRunFailure) { throw new AbortException("build step failed: " + e.getMessage()); } } if (ooBuildStepResult != null) { listener.getLogger().println("flow results:"); JAXB.marshal(ooBuildStepResult, listener.getLogger()); final EnvVars envVars = mapReturnValues(ooBuildStepResult, listener); build.addAction(new OOBuildStepResultsEnvironmentInjectionAction(envVars)); if (ooBuildStepResult.hasReport()) { // build.addAction(new OOBuildStepResultReportAction(runRequest.getFlow().getId(), ooBuildStepResult.getReportUrl())); build.addAction(new OOBuildStepResultReportAction(ooBuildStepResult.getReportUrl())); } listener.getLogger().println("***" + getRetVariableToCheck() + "***"); if (getRetVariableToCheck().length() > 0) { final String serverReturnedValue = envVars.get(getRetVariableToCheck()); if (serverReturnedValue == null) { throw new AbortException("could not check value of variable '" + getRetVariableToCheck() + "' - flow execution returned no such variable from the server.\n"); } else if (getMatchStrategy().matches(serverReturnedValue, getValueToCompareWith())) { listener.getLogger() .println("changing status of build to " + getDesiredResultType() + " because " + "value of " + getRetVariableToCheck() + " (=" + serverReturnedValue + ") " + getMatchStrategy() + " " + getValueToCompareWith() + "\n"); build.setResult(getDesiredResultType()); if (getDesiredResultType().equals(Result.FAILURE)) { throw new AbortException( "Aborting the build because flow failed and FAILURE was chosen.\n"); } } } //Set build status according to run status if (envVars.get("flowReturnCode").equals("Error")) { listener.annotate(new SimpleHtmlNote( "<font color=\"red\"><b>Run ended with status [" + envVars.get("flowReturnCode") + "] changing the build result to " + Result.FAILURE + "</b></font>\n")); build.setResult(Result.FAILURE); } else if (ExecutionResult.NO_ACTION_TAKEN .equals(envVars.get("flowReturnCode").equals("No Action"))) { listener.annotate(new SimpleHtmlNote( "<font color=\"yellow\"><b>Run ended with status [" + envVars.get("flowReturnCode") + "] changing the build result to " + Result.UNSTABLE + "</b></font>\n")); build.setResult(Result.UNSTABLE); } } } else { // post 10.10 invoke of a flow // get UUID of the selected flow String selectedFlowUUID = OOAccessibilityUtils.getFlowID10x(selectedFlowS, urlString + "/flows/library"); listener.getLogger() .println("The flow: " + selectedFlowS + " with UUID " + selectedFlowUUID + " is executing..."); listener.getLogger().getClass(); OOAccessibilityLayer ooAccessibilityLayer = new OOAccessibilityLayer(); String runName = resolveParametersInString(build, listener, this.runName); //trigger the flow TriggeredExecutionDetailsVO triggeredExecutionDetailsVO = ooAccessibilityLayer.run10x(selectedFlowUUID, argsToUse, urlString, stepExecutionTimeout, listener, runName); //Create report with link to run on OO build.addAction(new OOBuildStepResultReportAction(ooAccessibilityLayer.runURL)); //Wait for completion WaitExecutionResult waitExecutionResult = ooAccessibilityLayer .waitExecutionComplete(triggeredExecutionDetailsVO, stepExecutionTimeout, listener, urlString); //get step log List<StepLog> stepLog = ooAccessibilityLayer.getStepLog(waitExecutionResult.getStepCount(), triggeredExecutionDetailsVO.getFeedUrl()); //Handle build result if (waitExecutionResult.isTimedOut()) {//if timed out listener.annotate(new SimpleHtmlNote( "\n<font color=\"red\"><b>Timeout failure: The execution was not completed in the allotted time.\nLast status was: " + waitExecutionResult.getLastExecutionStatus() + " </b></font> \n\n")); build.setResult(Result.FAILURE); } else {//No time out listener.getLogger() .println("Run ended with status [" + waitExecutionResult.getFullStatusName() + "]"); if (!ExecutionStatusState.COMPLETED.equals(waitExecutionResult.getLastExecutionStatus())) {//If flow wasn't completed listener.annotate(new SimpleHtmlNote( "<font color=\"red\"><b>Run was not completed, changing the build result to " + Result.FAILURE + "</b></font>\n")); build.setResult(Result.FAILURE); } else {//flow was completed //Set build status according to run outputs if (stepLog.size() > 0) { StepLog lastStep = stepLog.get(stepLog.size() - 1); Map<String, String> lastRawResults = lastStep.getRawResult(); if (lastRawResults != null && lastRawResults.get(getRetVariableToCheck()) != null && !getRetVariableToCheck().isEmpty()) { //if run has the expected output if (getMatchStrategy().matches(lastRawResults.get(getRetVariableToCheck()), getValueToCompareWith())) { listener.getLogger().println("changing status of build to " + getDesiredResultType() + " because " + "value of " + getRetVariableToCheck() + " (" + lastRawResults.get(getRetVariableToCheck()) + ") " + getMatchStrategy() + " expected value of " + getValueToCompareWith()); build.setResult(getDesiredResultType()); } } else if (!getRetVariableToCheck().isEmpty()) { //flow output is empty or output wasn't found listener.annotate( new SimpleHtmlNote("<font color=\"red\"><b>could not check value of variable '" + getRetVariableToCheck() + "' - flow execution returned no such variable from the server.</b></font>\n")); } } //Set build status according to run status if (ExecutionResult.ERROR.equals(waitExecutionResult.getLastExecutionResult())) { //Print stack trace List<StepLog> stackTrace = ooAccessibilityLayer.createStackTrace(stepLog, "0"); listener.annotate(new SimpleHtmlNote(ooAccessibilityLayer.stackTraceAsString(stackTrace))); listener.annotate(new SimpleHtmlNote("<font color=\"red\"><b>Run ended with status [" + waitExecutionResult.getLastExecutionResult() + "] changing the build result to " + Result.FAILURE + "</b></font>\n")); build.setResult(Result.FAILURE); } else if (ExecutionResult.NO_ACTION_TAKEN .equals(waitExecutionResult.getLastExecutionResult())) { listener.annotate(new SimpleHtmlNote("<font color=\"yellow\"><b>Run ended with status [" + waitExecutionResult.getLastExecutionResult() + "] changing the build result to " + Result.UNSTABLE + "</b></font>\n")); build.setResult(Result.UNSTABLE); } } } //print the steps (convert step log to json) // listener.annotate(new SimpleHtmlNote(ooAccessibilityLayer.formatResult(jsonExecutionResult))); } return true; } private List<OOArg> expand(List<OOArg> args, AbstractBuild<MatrixProject, MatrixBuild> build, BuildListener listener) throws IOException, InterruptedException { if (args != null) { List<OOArg> expanded = new ArrayList<OOArg>(args.size()); EnvVars env = build.getEnvironment(listener); AbstractProject project = build.getProject(); listener.getLogger().println("Project name" + project.getDisplayName()); Map<AbstractProject, Integer> map = build.getUpstreamBuilds(); for (OOArg arg : args) { final String name = arg.getName(); final String value = arg.getValue(); final String nameExpansion = env.expand(name); final String valueExpansion = env.expand(value); final OOArg expandedArg = nameExpansion.equals(name) && valueExpansion.equals(value) ? arg : new OOArg(nameExpansion, valueExpansion); expanded.add(expandedArg); } return expanded; } else { // we need an arg even for a no-arg run : QCCR 24513 args = new ArrayList<OOArg>(); //args.add(0, new OOArg("defaultParamName", "defaultParamValue")); } return args; } private EnvVars mapReturnValues(OORunResponse ooBuildStepResult, BuildListener listener) { EnvVars vars = new EnvVars(); for (OORunResponse.OORunReturnItem item : ooBuildStepResult.getItems()) { final String name = item.getName(); final String value = item.getValue(); listener.getLogger().println("assigning: " + name + " = " + value); vars.put(name, value); } return vars; } public static URI URI(String urlString) { try { final URL url = new URL(urlString); return new URI(url.getProtocol(), null, url.getHost(), url.getPort(), url.getPath(), url.getQuery(), null); } catch (Exception e) { throw new RuntimeException(e); } } private static String encrypt(String password) { return isEncrypted(password) ? password : "MD5:" + TripleDES.encryptPassword(password); } private static String decrypt(String password) { return isEncrypted(password) ? TripleDES.decryptPassword(password.substring("MD5:".length())) : password; } private static boolean isEncrypted(String password) { return password.startsWith("MD5:"); } public void setStepExecutionTimeout(String stepExecutionTimeout) { this.stepExecutionTimeout = stepExecutionTimeout; } public String getStepExecutionTimeout() { return stepExecutionTimeout; } public String getRunName() { return runName; } protected String resolveParametersInString(Run<?, ?> build, TaskListener listener, String input) { try { return build.getEnvironment(listener).expand(input); } catch (Exception e) { listener.getLogger().println("Failed to resolve parameters in string \"" + input + "\" due to following error:\n" + e.getMessage()); } return input; } }