/******************************************************************************* * Copyright (c) 2014, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.team.build.internal.hjplugin; import hudson.AbortException; import hudson.model.TaskListener; import hudson.remoting.VirtualChannel; import hudson.util.Secret; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.util.Locale; import java.util.logging.Logger; import com.ibm.team.build.internal.hjplugin.RTCFacadeFactory.RTCFacadeWrapper; /** * Creates the build result or marks build result as started. Returns information * about who controls the build result lifecycle (Jenkins plugin/RTC), the build * result uuid and who is responsible for starting the build if started in RTC. * * This task groups work that should be run at the very start of the build whose * results are needed for record keeping. Additional work should be cautiously added * to this task since an exception (of any kind) in that will prevent lifecycle * info from being linked to the build. */ public class RTCBuildResultSetupTask extends RTCTask<BuildResultInfo> { private static final Logger LOGGER = Logger.getLogger(RTCBuildResultSetupTask.class.getName()); private final String buildToolkit; private final String serverURI; private final String userId; private final Secret password; private final int timeout; private final boolean useBuildDefinitionInBuild; private final String buildDefinition; private final String buildResultUUID; private final String label; private final TaskListener listener; private final boolean isRemote; private final boolean debug; private final Locale clientLocale; private final String contextStr; private static final long serialVersionUID = 1L; /** * Task that creates/starts a build result on the master or the slave * It also retrieves info related to the source of the build request * * @param contextStr Context for logging * @param buildToolkit The build toolkit to use when working with the facade * @param serverURI The address of the repository server * @param userId The user id to use when logging into the server * @param password The password to use when logging into the server. * @param timeout The timeout period for requests made to the server * @param buildDefinition The name (id) of the build definition to use. May be <code>null</code> * if buildWorkspace is supplied instead. * @param buildResultUUID The build result to relate build results with. * @param label The name to give the baselineSet created * @param listener A listener that will be notified of the progress and errors encountered. * @param isRemote Whether this will be executed on the Master or a slave * @param debug Whether to report debugging messages to the listener * @param clientLocale The locale of the requesting client */ public RTCBuildResultSetupTask(String contextStr, String buildToolkit, String serverURI, String userId, String password, int timeout, boolean useBuildDefinitionInBuild, String buildDefinition, String buildResultUUID, String label, TaskListener listener, boolean isRemote, boolean debug, Locale clientLocale) { super(debug, listener); this.contextStr = contextStr; this.buildToolkit = buildToolkit; this.serverURI = serverURI; this.userId = userId; this.password = Secret.fromString(password); this.timeout = timeout; this.useBuildDefinitionInBuild = useBuildDefinitionInBuild; this.buildDefinition = buildDefinition; this.buildResultUUID = buildResultUUID; this.label = label; this.listener = listener; this.isRemote = isRemote; this.debug = debug; this.clientLocale = clientLocale; } @Override public BuildResultInfo invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { if (debug) { debug("Build initialization for " + contextStr); //$NON-NLS-1$ debug("serverURI " + serverURI); //$NON-NLS-1$ debug("userId " + userId); //$NON-NLS-1$ debug("timeout " + timeout); //$NON-NLS-1$ debug("buildResult " + (buildResultUUID == null ? "n/a" : "defined")); //$NON-NLS-1$ debug("listener is " + (listener == null ? "null" : "not null")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ debug("Running remote " + isRemote); //$NON-NLS-1$ debug("buildToolkit property " + buildToolkit); //$NON-NLS-1$ } BuildResultInfo buildResultInfo = localInvocation(); if (buildResultInfo == null) { try { RTCFacadeWrapper facade = RTCFacadeFactory.getFacade(buildToolkit, debug ? listener.getLogger() : null); // If we don't have a build result but have a build definition, create the // build result prior to going to the slave so that it can be in the slave's // environment. Also we don't depend on slave giving it back and that helps // with terminating the build result during failure cases. if (buildResultUUID == null && useBuildDefinitionInBuild) { String actualBuildResultUUID = (String) facade.invoke( "createBuildResult", new Class[] { //$NON-NLS-1$ String.class, // serverURI, String.class, // userId, String.class, // password, int.class, // timeout, String.class, // buildDefinition, String.class, // buildLabel, Object.class, // listener Locale.class // clientLocale }, serverURI, userId, Secret.toString(password), timeout, buildDefinition, label, listener, clientLocale); buildResultInfo = new BuildResultInfo(actualBuildResultUUID, true); } else if (buildResultUUID != null) { // we own the build lifecycle if we start it. buildResultInfo = new BuildResultInfo(buildResultUUID, false); facade.invoke("startBuild", new Class[] { //$NON-NLS-1$ String.class, // serverURI, String.class, // userId, String.class, // password, int.class, // timeout, Object.class, // buildResultInfoObject, String.class, // buildLabel Object.class, // listener Locale.class // clientLocale }, serverURI, userId, Secret.toString(password), timeout, buildResultInfo, label, listener, clientLocale); } else { // we don't have a build result and we are using a workspace // should never get here since localInvocation handles this case // and ideally we aren't run remotely buildResultInfo = new BuildResultInfo(buildResultUUID, false); } } catch (Exception e) { // serious issue with the build setup - report it Throwable eToReport = e; if (eToReport instanceof InvocationTargetException && e.getCause() != null) { eToReport = e.getCause(); } if (eToReport instanceof InterruptedException) { listener.getLogger().println(Messages.RTCScm_checkout_failure3(eToReport.getMessage())); throw (InterruptedException) eToReport; } PrintWriter writer = listener.fatalError(Messages.RTCScm_checkout_failure3(eToReport.getMessage())); if (RTCScm.unexpectedFailure(eToReport)) { eToReport.printStackTrace(writer); } // if we can't establish the build result -> we can't build it throw new AbortException(Messages.RTCScm_checkout_failure4(eToReport.getMessage())); } } return buildResultInfo; } /** * If this task doesn't need to be run remotely give the appropriate answer back. * The task should run remotely if it needs to communicate with RTC server. * * @return <code>null</code> if the task needs to run remotely. Otherwise the * answer that can be determined locally. */ public BuildResultInfo localInvocation() { if (buildResultUUID == null && useBuildDefinitionInBuild) { return null; } else if (buildResultUUID != null) { return null; } else { // we don't have a build result and we are using a workspace return new BuildResultInfo(buildResultUUID, false); } } @Override protected Logger getLogger() { return LOGGER; } }