/******************************************************************************* * Copyright (c) 2013, 2016 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.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.model.TaskListener; import hudson.model.AbstractProject; import hudson.model.Cause; import hudson.model.CauseAction; import hudson.model.Hudson; import hudson.model.Job; import hudson.model.Node; import hudson.model.Queue.Task; import hudson.model.Run; import hudson.remoting.Channel; import hudson.remoting.RemoteOutputStream; import hudson.remoting.VirtualChannel; import hudson.scm.ChangeLogParser; import hudson.scm.PollingResult; import hudson.scm.PollingResult.Change; import hudson.scm.SCMDescriptor; import hudson.scm.SCMRevisionState; import hudson.scm.SCM; import hudson.security.ACL; import hudson.util.FormValidation; import hudson.util.Secret; import hudson.util.ListBoxModel; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.jvnet.localizer.LocaleProvider; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import com.cloudbees.plugins.credentials.CredentialsMatchers; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; import com.ibm.team.build.internal.hjplugin.RTCFacadeFactory.RTCFacadeWrapper; import com.ibm.team.build.internal.hjplugin.extensions.RtcExtensionProvider; import com.ibm.team.build.internal.hjplugin.util.Helper; import com.ibm.team.build.internal.hjplugin.util.RTCFacadeFacade; import com.ibm.team.build.internal.hjplugin.util.Tuple; import com.ibm.team.build.internal.hjplugin.util.ValidationResult; @ExportedBean(defaultVisibility=999) public class RTCScm extends SCM { private static final Logger LOGGER = Logger.getLogger(RTCScm.class.getName()); private static final String CALL_CONNECTOR_TIMEOUT_PROPERTY = "com.ibm.team.build.callConnector.timeout"; //$NON-NLS-1$ private static final String IGNORE_OUTGOING_FROM_BUILD_WS_WHILE_POLLING = "com.ibm.team.build.ignoreOutgoingFromBuildWorkspaceWhilePolling"; //$NON-NLS-1$ private static final String DEPRECATED_CREDENTIAL_EDIT_ALLOWED = "com.ibm.team.build.credential.edit"; //$NON-NLS-1$ private static final BigInteger BIGINT_ZERO = new BigInteger("0"); private static final BigInteger BIGINT_ONE = new BigInteger("1"); // persisted fields for SCM private boolean overrideGlobal; // Global setting that have been overridden by the job (if overrideGlobal is true) private String buildTool; private String serverURI; private int timeout; private String userId; private Secret password; private String passwordFile; private String credentialsId; public static final String BUILD_WORKSPACE_TYPE = "buildWorkspace"; //$NON-NLS-1$ public static final String BUILD_DEFINITION_TYPE = "buildDefinition"; //$NON-NLS-1$ public static final String BUILD_SNAPSHOT_TYPE = "buildSnapshot"; //$NON-NLS-1$ public static final String BUILD_STREAM_TYPE = "buildStream"; //$NON-NLS-1$ // constants for the possible values for snapshot owner type public static final String SNAPSHOT_OWNER_TYPE_NONE = "none"; //$NON-NLS-1$ public static final String SNAPSHOT_OWNER_TYPE_STREAM = "stream"; //$NON-NLS-1$ public static final String SNAPSHOT_OWNER_TYPE_WORKSPACE = "workspace"; //$NON-NLS-1$ // constants for the keys that identify various fields in the snapshot context map public static final String SNAPSHOT_OWNER_TYPE_KEY = "snapshotOwnerType"; //$NON-NLS-1$ public static final String PROCESS_AREA_OF_OWNING_STREAM_KEY = "processAreaOfOwningStream"; //$NON-NLS-1$ public static final String OWNING_STREAM_KEY = "owningStream"; //$NON-NLS-1$ public static final String OWNING_WORKSPACE_KEY = "owningWorkspace"; //$NON-NLS-1$ public static final int DEFAULT_SERVER_TIMEOUT = DescriptorImpl.DEFAULT_SERVER_TIMEOUT; // Job configuration settings private BuildType buildType; private String buildTypeStr; private String buildWorkspace; private String buildDefinition; private String buildSnapshot; private String buildStream; private String loadDirectory; private boolean clearLoadDirectory; private boolean createFoldersForComponents; private String componentsToExclude; // file having details on components to be excluded during load private String loadRules; // file having details on mapping between components and their load rules private String acceptBeforeLoad; private boolean generateChangelogWithGoodBuild; // Don't persist the browser because it references the server url that can be changing in the // global config. @Deprecated private transient RTCRepositoryBrowser browser; // Use rest or whatever services instead of the build toolkit. // Available in RTC 5.0 or 4.0.6 + retro-fitted changes private boolean avoidUsingToolkit; // process area associated with the configuration. Currently applicable only to stream configuration and it is used // to lookup the stream by name. private String processArea; // see the config file for significance of this field private String currentSnapshotOwnerType; // object containing the snapshot owner details private BuildSnapshotContext buildSnapshotContext; // determines if we have to override the default name given to the snapshot that is created after accepting the // changes private boolean overrideDefaultSnapshotName; // snapshot name, configured in the job, that overrides the default name given to the snapshot that is created after // accepting the changes private String customizedSnapshotName; /** * Class defining the snapshot context configuration data. */ public static class BuildSnapshotContext { public String snapshotOwnerType; public String processAreaOfOwningStream; public String owningStream; public String owningWorkspace; @DataBoundConstructor public BuildSnapshotContext(String snapshotOwnerType, String processAreaOfOwningStream, String owningStream, String owningWorkspace) { this.snapshotOwnerType = snapshotOwnerType; this.processAreaOfOwningStream = processAreaOfOwningStream; this.owningStream = owningStream; this.owningWorkspace = owningWorkspace; } /** * Construct the map with the current instance. * */ public Map<String, String> getContextMap() { return getBuildSnapshotContextMap(snapshotOwnerType, processAreaOfOwningStream, owningStream, owningWorkspace); } /** * Construct the map with the provided details. * * Snapshot context is a complex datastructure. Since complex datastructures cannot be shared through a data * model between hjplugin.jar and hjplugin-rtc.jar, the complex data structure is converted to a map of fields * and values; this map, that is sent by hjplugin.jar, is read and converted to a object by hjplugin-rtc.jar. * Though this is hacky, it is better than passing individual fields. This class has to be retained until we * design a permanent solution to pass complex data structures between the two jars. */ public static Map<String, String> getBuildSnapshotContextMap(String snapshotOwnerType, String processAreaOfOwningStream, String owningStream, String owningWorkspace) { Map<String, String> contextMap = new HashMap<String, String>(); contextMap.put(SNAPSHOT_OWNER_TYPE_KEY, snapshotOwnerType); contextMap.put(PROCESS_AREA_OF_OWNING_STREAM_KEY, processAreaOfOwningStream); contextMap.put(OWNING_STREAM_KEY, owningStream); contextMap.put(OWNING_WORKSPACE_KEY, owningWorkspace); return contextMap; } } /** * Structure that represents the radio button selection for build workspace/definition * choice in config.jelly (job configuration) */ public static class BuildType { public String value; public String buildDefinition; public String buildWorkspace; public String buildSnapshot; public String buildStream; public String loadDirectory; public boolean clearLoadDirectory; public boolean createFoldersForComponents; public String componentsToExclude; public String loadRules; private String acceptBeforeLoad = "true"; private boolean generateChangelogWithGoodBuild; private String processArea; private String currentSnapshotOwnerType; private BuildSnapshotContext buildSnapshotContext; private boolean overrideDefaultSnapshotName; private String customizedSnapshotName; @DataBoundConstructor public BuildType(String value, String buildDefinition, String buildWorkspace, String buildSnapshot, String buildStream) { this.value = value; this.buildDefinition = buildDefinition; this.buildWorkspace = buildWorkspace; this.buildSnapshot = buildSnapshot; this.buildStream = buildStream; } @DataBoundSetter public void setLoadDirectory(String loadDirectory) { this.loadDirectory = loadDirectory; } public String getLoadDirectory() { return this.loadDirectory; } @DataBoundSetter public void setClearLoadDirectory(boolean clearLoadDirectory) { this.clearLoadDirectory = clearLoadDirectory; } public boolean getClearLoadDirectory() { return clearLoadDirectory; } @DataBoundSetter public void setCreateFoldersForComponents(boolean createFoldersForComponents) { this.createFoldersForComponents = createFoldersForComponents; } public boolean getCreateFoldersForComponents() { return createFoldersForComponents; } public void setComponentsToExclude(String componentsToExclude) { this.componentsToExclude = componentsToExclude; } public String getComponentsToExclude() { return componentsToExclude; } public void setLoadRules(String loadRules) { this.loadRules = loadRules; } public String getLoadRules() { return loadRules; } @DataBoundSetter public void setAcceptBeforeLoad(boolean acceptBeforeLoad) { this.acceptBeforeLoad = String.valueOf(acceptBeforeLoad); } public boolean getAcceptBeforeLoad() { return Boolean.parseBoolean(acceptBeforeLoad); } public boolean getGenerateChangelogWithGoodBuild() { return generateChangelogWithGoodBuild; } @DataBoundSetter public void setGenerateChangelogWithGoodBuild(boolean generateChangelogWithGoodBuild) { this.generateChangelogWithGoodBuild = generateChangelogWithGoodBuild; } @DataBoundSetter public void setProcessArea(String processArea) { this.processArea = processArea; } public String getProcessArea() { return processArea; } @DataBoundSetter public void setCurrentSnapshotOwnerType(String currentSnapshotOwnerType) { this.currentSnapshotOwnerType = currentSnapshotOwnerType; } public String getCurrentSnapshotOwnerType() { return currentSnapshotOwnerType; } @DataBoundSetter public void setBuildSnapshotContext(BuildSnapshotContext buildSnapshotContext) { this.buildSnapshotContext = buildSnapshotContext; } public BuildSnapshotContext getBuildSnapshotContext() { return buildSnapshotContext; } @DataBoundSetter public void setOverrideDefaultSnapshotName(boolean overrideDefaultSnapshotName) { this.overrideDefaultSnapshotName = overrideDefaultSnapshotName; } public boolean getOverrideDefaultSnapshotName() { return overrideDefaultSnapshotName; } @DataBoundSetter public void setCustomizedSnapshotName(String customizedSnapshotName) { this.customizedSnapshotName = customizedSnapshotName; } public String getCustomizedSnapshotName() { return customizedSnapshotName; } } // Descriptor class - contains the global configuration settings @Extension public static class DescriptorImpl extends SCMDescriptor<RTCScm> { private static final String DEFAULT_SERVER_URI = "https://localhost:9443/ccm"; //$NON-NLS-1$ private static final int DEFAULT_SERVER_TIMEOUT = 480; private static transient boolean deprecatedCredentialEditAllowed = Boolean.getBoolean(DEPRECATED_CREDENTIAL_EDIT_ALLOWED); // persisted fields private String globalBuildTool; private String globalServerURI; private int globalTimeout; private String globalCredentialsId; // explicit UserId & password or password file private String globalUserId; private Secret globalPassword; private String globalPasswordFile; // Use rest or whatever services instead of the build toolkit. // Available in RTC 5.0 or 4.0.6 + retro-fitted changes private boolean globalAvoidUsingToolkit; public DescriptorImpl() { super(RTCScm.class, RTCRepositoryBrowser.class); load(); } @Override public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException { RTCScm scm = (RTCScm) super.newInstance(req, formData); new RTCRepositoryBrowser(scm.getServerURI()); return scm; } @Override public String getDisplayName() { return Messages.RTCScm_RTC_display_name(); } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { LOGGER.finest("DescriptorImpl.configure: Begin"); globalBuildTool = Util.fixEmptyAndTrim(json.optString("buildTool")); //$NON-NLS-1$ globalServerURI = Util.fixEmptyAndTrim(json.optString("serverURI")); //$NON-NLS-1$ globalUserId = Util.fixEmptyAndTrim(json.optString("userId")); //$NON-NLS-1$ String timeout = json.optString("timeout"); //$NON-NLS-1$ globalAvoidUsingToolkit = json.containsKey("avoidUsingToolkit"); //$NON-NLS-1$ try { globalTimeout = timeout == null ? 0 : Integer.parseInt(timeout); } catch (NumberFormatException e) { globalTimeout = 0; } String password = json.optString("password"); //$NON-NLS-1$ globalPassword = password == null || password.length() == 0 ? null : Secret.fromString(password); globalPasswordFile = Util.fixEmptyAndTrim(json.optString("passwordFile")); //$NON-NLS-1$ globalCredentialsId = Util.fixEmptyAndTrim(json.optString("credentialsId")); //$NON-NLS-1$ if (globalCredentialsId != "") { // They are saving with credentials. Remove the vestiges of the old authentication method globalPassword = null; globalPasswordFile = null; globalUserId = null; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("configure : " + //$NON-NLS-1$ "\" globalServerURI=\"" + globalServerURI + //$NON-NLS-1$ "\" globalUserid=\"" + globalUserId + //$NON-NLS-1$ "\" globalTimeout=\"" + globalTimeout + //$NON-NLS-1$ "\" globalPassword " + (globalPassword == null ? "is not supplied" //$NON-NLS-1$ //$NON-NLS-2$ : "(" + Secret.toString(globalPassword).length() + " characters)") + //$NON-NLS-1$ //$NON-NLS-2$ " globalPasswordFile=\"" + globalPasswordFile + //$NON-NLS-1$ //$NON-NLS-2$ "\" globalCredentialsId=\"" + globalCredentialsId + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } save(); return true; } public String getGlobalBuildTool() { return globalBuildTool; } /** * Determine if they are using the deprecated authentication way * @return <code>true</code> if using user id & password or password file * <code>false</code> if using credentials or no password authentication setup. */ public boolean usingDeprecatedPassword() { if (deprecatedCredentialEditAllowed()) { return true; } String globalCredId = getGlobalCredentialsId(); String userIdToUse = getGlobalUserId(); String passwordToUse = getGlobalPassword(); String passwordFileToUse = getGlobalPasswordFile(); if (globalCredId == null || globalCredId.isEmpty()) { // consider them to be using user id & old password way if supplied // not using strict validation since we would still work if too much info supplied. if (userIdToUse != null && !userIdToUse.isEmpty() && ((passwordToUse != null && !passwordToUse.isEmpty()) || (passwordFileToUse != null && !passwordFileToUse.isEmpty()))) { return true; } } return false; } /** * @return Whether the global password file should be shown in the UI */ public boolean showGlobalPasswordFile() { String passwordFileToUse = getGlobalPasswordFile(); if (deprecatedCredentialEditAllowed() || (passwordFileToUse != null && !passwordFileToUse.isEmpty())) { return true; } return false; } /** * @return Whether the global password should be shown in the UI */ public boolean showGlobalPassword() { String passwordToUse = getGlobalPassword(); if (deprecatedCredentialEditAllowed() || (passwordToUse != null && !passwordToUse.isEmpty())) { return true; } return false; } public boolean deprecatedCredentialEditAllowed() { return deprecatedCredentialEditAllowed; } public String getGlobalCredentialsId() { return globalCredentialsId; } public String getGlobalPassword() { String result; if (globalPassword == null) { result = null; } else { result = globalPassword.getPlainText(); } return result; } public String getGlobalPasswordFile() { return globalPasswordFile; } public String getGlobalServerURI() { if (globalServerURI == null || globalServerURI.length() == 0) { return DEFAULT_SERVER_URI; } return globalServerURI; } public String getGlobalUserId() { return globalUserId; } public int getGlobalTimeout() { if (globalTimeout == 0) { return DEFAULT_SERVER_TIMEOUT; } return globalTimeout; } public boolean getGlobalAvoidUsingToolkit() { return globalAvoidUsingToolkit; } /** * Get the path on the Master to the build toolkit for the given Build tool. * @param buildTool The id of the build tool * @param listener Listener to log any problems encountered. * @return Path to the Build Toolkit directory * @throws IOException * @throws InterruptedException */ public String getMasterBuildToolkit(String buildTool, TaskListener listener) throws IOException, InterruptedException { return getBuildToolkit(buildTool, Hudson.getInstance(), listener); } @Override public boolean isApplicable(Job project) { return true; } /** * For a given build tool on a given node, resolve what directory is expected to contains the build toolkit. * Jenkins provides tool installation abilities as well as a way of describing where a tool is located on * as Slave. We will delegate the path resolution to Jenkins. * @param buildTool The build tool that we want the path for * @param node The node (Master or a particular slave) the build toolkit will used on. * @param listener Listener to log any problems encountered. * @return Path to the Build Toolkit directory * @throws IOException * @throws InterruptedException */ private String getBuildToolkit(String buildTool, Node node, TaskListener listener) throws IOException, InterruptedException { RTCBuildToolInstallation[] installations = RTCBuildToolInstallation.allInstallations(); for (RTCBuildToolInstallation buildToolIntallation : installations) { if (buildToolIntallation.getName().equals(buildTool)) { return buildToolIntallation.forNode(node, listener).getHome(); } } return null; } /** * Provides a listbox of the defined build tools to pick from. Also includes * an entry to signify no toolkit is chosen. * @return The valid build tool options */ public ListBoxModel doFillBuildToolItems() { ListBoxModel listBox = new ListBoxModel(); listBox.add(new ListBoxModel.Option(Messages.RTCScm_no_build_tool_name(), "")); RTCBuildToolInstallation[] allTools = RTCBuildToolInstallation.allInstallations(); for (RTCBuildToolInstallation tool : allTools) { ListBoxModel.Option option = new ListBoxModel.Option(tool.getName()); listBox.add(option); } return listBox; } public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Job<?, ?> project, @QueryParameter String serverURI) { return new StandardListBoxModel() .withEmptySelection() .withMatching(CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, project, ACL.SYSTEM, URIRequirementBuilder.fromUri(serverURI).build())); // TODO look into us being able to support certificates // return new StandardListBoxModel() // .withEmptySelection() // .withMatching(CredentialsMatchers.anyOf( // CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), // CredentialsMatchers.instanceOf(StandardCertificateCredentials.class)), // CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, project, ACL.SYSTEM, URIRequirementBuilder.fromUri(url).build())); } /** * Called from the forms to validate the timeout value. * @param timeout The timeout value. * @return Whether the timeout is valid or not. Never <code>null</code> */ public FormValidation doCheckTimeout(@QueryParameter String timeout) { return RTCLoginInfo.validateTimeout(timeout); } /** * Called from the forms to validate that the build tool is selected * and that the underlying build tool points to a valid build toolkit. * @param buildTool The name of the build tool to validate * @return Whether the build tool is valid or not. Never <code>null</code> */ public FormValidation doCheckBuildTool( @QueryParameter("buildTool") String buildTool) { LOGGER.finest("DescriptorImpl.doCheckBuildTool: Begin"); if (Util.fixEmptyAndTrim(buildTool) == null) { return FormValidation.error(Messages.RTCScm_build_tool_needed_for_job()); } // validate the build toolkit path String buildToolkitPath; try { buildToolkitPath = getMasterBuildToolkit(buildTool, TaskListener.NULL); } catch (Exception e) { return FormValidation.error(e, Messages.RTCScm_no_build_toolkit(e.getMessage())); } FormValidation buildToolkitCheck = RTCBuildToolInstallation.validateBuildToolkit(false, buildToolkitPath); if (!buildToolkitCheck.kind.equals(FormValidation.Kind.OK)) { return buildToolkitCheck; } return FormValidation.ok(); } public FormValidation doCheckCredentialsId( @QueryParameter("credentialsId") String credentialsId, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile) { LOGGER.finest("DescriptorImpl.doCheckCredentialsId: Begin"); return RTCLoginInfo.validateCredentials(credentialsId, userId, passwordFile, password); } public FormValidation doCheckUserId( @QueryParameter("credentialsId") String credentialsId, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile) { return RTCLoginInfo.validateUserId(credentialsId, userId, passwordFile, password); } public FormValidation doCheckPassword( @QueryParameter("credentialsId") String credentialsId, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile) { return RTCLoginInfo.validatePassword(credentialsId, userId, passwordFile, password); } public FormValidation doCheckPasswordFile( @QueryParameter("credentialsId") String credentialsId, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile) { return RTCLoginInfo.validatePasswordFile(credentialsId, userId, passwordFile, password); } /** * For the default global configuration check that the connection works (with whatever * access preferred). Main idea is check server, user credentials are valid for logging in. * @param buildTool The build tool selected to be used in builds * @param serverURI The RTC server uri * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id * @param password The password to use when logging in to RTC. Must supply this or a password file * if the credentials id was not supplied. * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or * a password if the credentials id was not supplied. * @param credId Credential id that will identify the user id and password to use * @param timeout The timeout period for the connection * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the connection. * @return The result of the validation (will be ok if valid) */ public FormValidation doCheckGlobalConnection( @QueryParameter("buildTool") final String buildTool, @QueryParameter("serverURI") final String serverURI, @QueryParameter("userId") final String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("timeout") final String timeout, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit) { LOGGER.finest("DescriptorImpl.doCheckGlobalConnection: Begin"); boolean avoidUsingToolkit = Boolean.parseBoolean(Util.fixNull(avoidUsingBuildToolkit)); ValidationResult result = validateConnectInfo(null, true, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); // validate the connection if (result.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return result.validationResult; } else { FormValidation connectCheck = checkConnect(result.buildToolkitPath, avoidUsingToolkit, result.loginInfo); return Helper.mergeValidationResults(result.validationResult, connectCheck); } } /** * For the job configuration check that the connection works (with whatever * access preferred). Use either the information supplied or the previously stored * global information depending on whether to override the global information. * Main idea is check server, user credentials are valid for logging in. * @param override Whether to use the global settings or not * @param buildTool The build tool selected to be used in builds * @param serverURI The RTC server uri * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id * @param password The password to use when logging in to RTC. Must supply this or a password file * if the credentials id was not supplied. * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or * a password if the credentials id was not supplied. * @param credId Credential id that will identify the user id and password to use * @param timeout The timeout period for the connection * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the connection. * @return The result of the validation (will be ok if valid) */ public FormValidation doCheckJobConnection( @AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("credentialsId") String credId, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("timeout") String timeout, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit) { LOGGER.finest("DescriptorImpl.doCheckJobConnection: Begin"); boolean overrideGlobal = Boolean.parseBoolean(Util.fixNull(override)); boolean avoidUsingToolkit; if (!overrideGlobal) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(Util.fixNull(avoidUsingBuildToolkit)); } ValidationResult result = validateConnectInfo(project, !overrideGlobal, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); // validate the connection if (result.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return result.validationResult; } else { FormValidation connectCheck = checkConnect(result.buildToolkitPath, avoidUsingToolkit, result.loginInfo); return Helper.mergeValidationResults(result.validationResult, connectCheck); } } /** * Check that we can connect to the RTC server. If avoiding the toolkit, we * will not validate it since it will not be used. * @param checkingGlobalSettings Whether the settings are global or job. Affects * the error message given. * @param buildTool The build tool configured. Only used if avoidUsingBuildToolkit is off * @param serverURI The RTC server to connect to * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id * @param password The password to use when logging in to RTC. Must supply this or a password file * if the credentials id was not supplied. * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or * a password if the credentials id was not supplied. * @param credId Credential id that will identify the user id and password to use * @param timeout The timeout period for the connection * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the connection. * @return The result of the validation (will be ok if valid) */ private ValidationResult validateConnectInfo( final Job<?,?> project, final boolean checkingGlobalSettings, final String buildTool, final String serverURI, final String userId, final String password, String passwordFile, String credId, final String timeout, final boolean avoidUsingToolkit) { LOGGER.finest("DescriptorImpl.validateConnectInfo: Begin"); passwordFile = Util.fixEmptyAndTrim(passwordFile); credId = Util.fixEmptyAndTrim(credId); ValidationResult result = new ValidationResult(); // in the global case the build tool doesn't need to really be supplied. It could be supplied // by the Job. However, if they want to check the connection, they would need to have avoid using toolkit // enabled and not use a password file. boolean warnOnly = avoidUsingToolkit && (credId != null || passwordFile == null); // we need the build tool to validate if (Util.fixEmptyAndTrim(buildTool) == null) { String errorMessage; if (warnOnly) { if (checkingGlobalSettings) { errorMessage = Messages.RTCScm_global_build_tool_needed_for_job(); } else { errorMessage = Messages.RTCScm_build_tool_needed_for_job(); } result.validationResult = FormValidation.warning(errorMessage); } else { if (checkingGlobalSettings) { errorMessage = Messages.RTCScm_missing_global_build_tool(); } else { errorMessage = Messages.RTCScm_missing_build_tool(); } result.validationResult = FormValidation.error(errorMessage); return result; } } else { // validate the build toolkit path try { result.buildToolkitPath = getMasterBuildToolkit(buildTool, TaskListener.NULL); } catch (Exception e) { String errorMessage; if (checkingGlobalSettings) { errorMessage = Messages.RTCScm_no_global_build_toolkit3(e.getMessage()); } else { errorMessage = Messages.RTCScm_no_build_toolkit(e.getMessage()); } if (warnOnly) { result.validationResult = FormValidation.warning(errorMessage); } else { result.validationResult = FormValidation.error(errorMessage); } return result; } result.validationResult = RTCBuildToolInstallation.validateBuildToolkit(warnOnly, result.buildToolkitPath); if (result.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return result; } } // validate the authentication information FormValidation basicValidate = RTCLoginInfo.basicValidate(credId, userId, passwordFile, password, timeout); if (basicValidate.kind.equals(FormValidation.Kind.ERROR)) { result.validationResult = Helper.mergeValidationResults(result.validationResult, basicValidate); return result; } try { result.loginInfo = new RTCLoginInfo(project, result.buildToolkitPath, serverURI, userId, password, passwordFile, credId, Integer.parseInt(timeout)); } catch (InvalidCredentialsException e) { result.validationResult = FormValidation.error(e, e.getMessage()); } return result; } /** * Check the connection to the RTC server. This will validate that we can connect to the server with the * credentials defined. Connection may be through the toolkit or Rest services. This means a version * compatibility check is also done. * @param buildToolkitPath Path to the build toolkit * @param avoidUsingTookit Whether to try to avoid using the build toolkit * @param loginInfo The credentials for logging into the server. * @return Whether the connection is valid or not. Never <code>null</code> */ private FormValidation checkConnect(String buildToolkitPath, boolean avoidUsingTookit, RTCLoginInfo loginInfo) { LOGGER.finest("DescriptorImpl.checkConnect: Begin"); try { String errorMessage = RTCFacadeFacade.testConnection(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingTookit); if (errorMessage != null && errorMessage.length() != 0) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkConnect attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkConnect invocation failure " + eToReport.getMessage(), eToReport); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkConnect attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkConnect failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, Messages.RTCScm_failed_to_connect(e.getMessage())); } return FormValidation.ok(Messages.RTCScm_connect_success()); } /** * Validates the build workspace configuration. Called by the forms. * Fields validated apart from connection info - Build Workspace. * * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file * if the credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or * a password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the connection. (Job setting) * @param buildWorkspace The build workspace to validate * @return Whether the build workspace configuration is valid or not. Never <code>null</code> */ public FormValidation doValidateBuildWorkspaceConfiguration( @AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit, @QueryParameter("buildWorkspace") String buildWorkspace) { LOGGER.finest("DescriptorImpl.doValidateBuildWorkspaceConfiguration: Begin"); //$NON-NLS-1$ // validate if required fields are provided if (Util.fixEmptyAndTrim(buildWorkspace) == null) { return FormValidation.error(Messages.RTCScm_build_workspace_empty()); } boolean overrideGlobalSettings = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobalSettings) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // validate the info for connecting to the server (including toolkit & auth info). ValidationResult connectInfoCheck = validateConnectInfo(project, !overrideGlobalSettings, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectInfoCheck.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectInfoCheck.validationResult; } // connection info is good now validate the build workspace boolean parameterizedWorkspace = Helper.isAParameter(buildWorkspace); FormValidation buildWorkspaceCheck = null; if (parameterizedWorkspace) { buildWorkspaceCheck = FormValidation.warning(Messages.RTCScm_repository_workspace_not_validated()); } else { buildWorkspaceCheck = checkBuildWorkspace(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, buildWorkspace); } // If the build workspace validation completed with OK then recreate the result with a configuration valid // message if(buildWorkspaceCheck.kind.equals(FormValidation.Kind.OK)) { buildWorkspaceCheck = FormValidation.ok(Messages.RTCScm_validation_success()); } return Helper.mergeValidationResults(connectInfoCheck.validationResult, buildWorkspaceCheck); } /** * Validates the build definition configuration. Called by the forms. * Fields validated apart from the connection info - Build Definition. * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file * if the credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or * a password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the connection. (Job setting) * @param buildDefinition The build definition to validate * @return Whether the build definition configuration is valid or not. Never <code>null</code> */ public FormValidation doValidateBuildDefinitionConfiguration( @AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") final String avoidUsingBuildToolkit, @QueryParameter("buildDefinition") final String buildDefinition) { LOGGER.finest("DescriptorImpl.doValidateBuildDefinitionConfiguration: Begin"); //$NON-NLS-1$ // validate if required fields are provided if (Util.fixEmptyAndTrim(buildDefinition) == null) { return FormValidation.error(Messages.RTCScm_build_definition_empty()); } boolean overrideGlobalSettings = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobalSettings) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // validate the info for connecting to the server (including toolkit & auth info). ValidationResult connectInfoCheck = validateConnectInfo(project, !overrideGlobalSettings, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectInfoCheck.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectInfoCheck.validationResult; } // connection info is good now validate the build definition boolean parameterizedBuildDefinition = Helper.isAParameter(buildDefinition); FormValidation buildDefinitionCheck = null; if (parameterizedBuildDefinition) { buildDefinitionCheck = FormValidation.warning(Messages.RTCScm_build_definition_not_validated()); } else { buildDefinitionCheck = checkBuildDefinition(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, buildDefinition); } // If the build definition validation completed with OK then recreate the result with a configuration valid // message if (buildDefinitionCheck.kind.equals(FormValidation.Kind.OK)) { buildDefinitionCheck = FormValidation.ok(Messages.RTCScm_validation_success()); } return Helper.mergeValidationResults(connectInfoCheck.validationResult, buildDefinitionCheck); } /** * Validates the build stream configuration. Called by the forms. Fields validated apart from the connection * info - Project or Team Area(if specified), Build Stream. * * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file if the * credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or a * password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the * connection. (Job setting) * @param processArea Project or Team Area owning the stream * @param buildStream The build stream to validate * @return Whether the build stream configuration is valid or not. Never <code>null</code> */ public FormValidation doValidateBuildStreamConfiguration( @AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") final String avoidUsingBuildToolkit, @QueryParameter("processArea") final String processArea, @QueryParameter("buildStream") final String buildStream) { LOGGER.finest("DescriptorImpl.doValidateBuildStreamConfiguration : Enter"); //$NON-NLS-1$ // validate if required fields are provided if (Util.fixEmptyAndTrim(buildStream) == null) { return FormValidation.error(Messages.RTCScm_build_stream_empty()); } boolean overrideGlobal = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobal) { // use the global settings buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // First do a connection check, then check the process area and stream ValidationResult connectionInfoResult = validateConnectInfo(project, !overrideGlobal, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectionInfoResult.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectionInfoResult.validationResult; } // build toolkit is required to validate process area if (avoidUsingToolkit && connectionInfoResult.buildToolkitPath == null && Util.fixEmptyAndTrim(processArea) != null) { return Helper.mergeValidationResults(connectionInfoResult.validationResult, FormValidation.error(Messages.RTCScm_build_toolkit_required_to_validate_process_area())); } // no error, proceed to check the stream. checking the stream in turn validates project area existence boolean parameterizedStream = Helper.isAParameter(buildStream); FormValidation streamValidationResult = null; if (parameterizedStream) { // validate owner details for parameterized stream FormValidation ownerValidationResult = FormValidation.ok(); if (Util.fixEmptyAndTrim(processArea) != null) { ownerValidationResult = checkProcessArea(connectionInfoResult.buildToolkitPath, connectionInfoResult.loginInfo, processArea); } // error fail right away if (ownerValidationResult.kind.equals(FormValidation.Kind.ERROR)) { return Helper.mergeValidationResults(connectionInfoResult.validationResult, ownerValidationResult); } // warn that parameterized values cannot be validated, the configuration is still valid from validation // perspective streamValidationResult = Helper.mergeValidationResults(ownerValidationResult, FormValidation.warning(Messages.RTCScm_stream_not_validated())); } else { streamValidationResult = checkBuildStream(connectionInfoResult.buildToolkitPath, avoidUsingToolkit, connectionInfoResult.loginInfo, processArea, buildStream); } // If the build stream validation completed with OK then recreate the result with a configuration valid // message if (streamValidationResult.kind.equals(FormValidation.Kind.OK)) { streamValidationResult = FormValidation.ok(Messages.RTCScm_validation_success()); } return Helper.mergeValidationResults(connectionInfoResult.validationResult, streamValidationResult); } /** * Validates the build snapshot configuration. Called by the forms. Fields validated apart from the connection * info - Project or Team Area(if specified), Build Snapshot. * * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file if the * credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or a * password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the * connection. (Job setting) * @param currentSnapshotOwnerType Currently selected snapshot owner type - "none", "stream", or "workspace" * @param processAreaOfOwningStream Name of the project or team area of the stream owning the snapshot * @param owningStream Name of the stream owning the snapshot * @param owningWorkspace Name of the workspace owning the snapshot * @param buildSnapshot The build snapshot to validate * * @return Whether the build snapshot configuration is valid or not. Never <code>null</code> */ public FormValidation doValidateBuildSnapshotConfiguration(@AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit, @QueryParameter("currentSnapshotOwnerType") String currentSnapshotOwnerType, @QueryParameter("processAreaOfOwningStream") String processAreaOfOwningStream, @QueryParameter("owningStream") String owningStream, @QueryParameter("owningWorkspace") String owningWorkspace, @QueryParameter("buildSnapshot") String buildSnapshot) { LOGGER.finest("DescriptorImpl.doValidateBuildSnapshotConfiguration: Begin"); //$NON-NLS-1$ // validate if a value is specified for build snapshot if (Util.fixEmptyAndTrim(buildSnapshot) == null) { return FormValidation.error(Messages.RTCScm_build_snapshot_empty()); } boolean overrideGlobalSettings = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobalSettings) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // validate the info for connecting to the server (including toolkit // & auth info). ValidationResult connectInfoCheck = validateConnectInfo(project, !overrideGlobalSettings, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectInfoCheck.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectInfoCheck.validationResult; } // build toolkit is required to validate the snapshot configuration if (avoidUsingToolkit && connectInfoCheck.buildToolkitPath == null) { return Helper.mergeValidationResults(connectInfoCheck.validationResult, FormValidation.error(Messages.RTCScm_build_toolkit_required_to_validate_snapshot())); } // validate snapshot boolean parameterizedSnapshot = Helper.isAParameter(buildSnapshot); FormValidation buildSnapshotValidationResult = null; if (parameterizedSnapshot) { // validate snapshot owner details for parameterized snapshot FormValidation ownerValidationResult = FormValidation.ok(); if (Util.fixEmptyAndTrim(currentSnapshotOwnerType) != null) { if (SNAPSHOT_OWNER_TYPE_WORKSPACE.equals(currentSnapshotOwnerType) && Util.fixEmptyAndTrim(owningWorkspace) != null) { ownerValidationResult = checkBuildWorkspace(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, owningWorkspace); } else if (SNAPSHOT_OWNER_TYPE_STREAM.equals(currentSnapshotOwnerType)) { if (Util.fixEmptyAndTrim(owningStream) != null) { ownerValidationResult = checkBuildStream(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, processAreaOfOwningStream, owningStream); } else if (Util.fixEmptyAndTrim(processAreaOfOwningStream) != null) { ownerValidationResult = checkProcessArea(connectInfoCheck.buildToolkitPath, connectInfoCheck.loginInfo, processAreaOfOwningStream); } } } // error fail right away if (ownerValidationResult.kind.equals(FormValidation.Kind.ERROR)) { return Helper.mergeValidationResults(connectInfoCheck.validationResult, ownerValidationResult); } // warn that parameterized values cannot be validated, the configuration is still valid from validation // perspective buildSnapshotValidationResult = Helper.mergeValidationResults(ownerValidationResult, FormValidation.warning(Messages.RTCScm_build_snapshot_not_validated())); } else { buildSnapshotValidationResult = checkBuildSnapshot(connectInfoCheck.buildToolkitPath, connectInfoCheck.loginInfo, BuildSnapshotContext.getBuildSnapshotContextMap(currentSnapshotOwnerType, processAreaOfOwningStream, owningStream, owningWorkspace), buildSnapshot); } // If the build snapshot validation completed with OK then recreate the result with a configuration valid // message if (buildSnapshotValidationResult.kind.equals(FormValidation.Kind.OK)) { buildSnapshotValidationResult = FormValidation.ok(Messages.RTCScm_validation_success()); } return Helper.mergeValidationResults(connectInfoCheck.validationResult, buildSnapshotValidationResult); } /** * Validate the list of components to exclude. * * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file if the * credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or a * password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the * connection. (Job setting) * @param processArea The name of the project or team area * @param buildTypeStr Type of the build configuration * @param buildWorkspace The workspace to build from * @param buildStream The stream to build from * @param componentsToExclude Path to the file specifying the list of components to exclude * * @return Whether the list of components to exclude is valid or not. Never <code>null</code> */ public FormValidation doValidateComponentsToExclude(@AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit, @QueryParameter("processArea") String processArea, @QueryParameter("value") String buildTypeStr, @QueryParameter("buildWorkspace") String buildWorkspace, @QueryParameter("buildStream") String buildStream, @QueryParameter("componentsToExclude") String componentsToExclude) { LOGGER.finest("DescriptorImpl.doValidateComponentsToExclude: Begin"); //$NON-NLS-1$ // first validate if the user has specified the components to // exclude file path if (componentsToExclude == null || componentsToExclude.trim().length() == 0) { return FormValidation.error(Messages.RTCScm_components_to_exclude_specify_file_path()); } // validate if the required fields are provided // this validation does not require any server call, so it is better // to fail upright than failing after validating other details like // workspace which requires a server call String componentsToExcludeJson = null; try { componentsToExcludeJson = Helper.validateAndGetComponentsToExcludeJson(componentsToExclude); } catch (Exception e) { return FormValidation.error(e, e.getMessage()); } boolean overrideGlobalSettings = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobalSettings) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // validate the info for connecting to the server (including toolkit // & auth info). ValidationResult connectInfoCheck = validateConnectInfo(project, !overrideGlobalSettings, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectInfoCheck.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectInfoCheck.validationResult; } // connect info is good, now validate the list of components to // exclude boolean isStreamConfiguration = BUILD_STREAM_TYPE.equals(buildTypeStr); if (isStreamConfiguration) { buildWorkspace = buildStream; } FormValidation componentsToExcludeCheck = checkComponentsToExclude(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, processArea, isStreamConfiguration, buildWorkspace, componentsToExcludeJson); return Helper.mergeValidationResults(connectInfoCheck.validationResult, componentsToExcludeCheck); } /** * Validate the component-to-load-rule file mapping. * * @param project The Job that is going to be run * @param override Whether to override the global connection settings * @param buildTool The build tool selected to be used in builds (Job setting) * @param serverURI The RTC server uri (Job setting) * @param userId The user id to use when logging in to RTC. Must supply this or a credentials id (Job setting) * @param password The password to use when logging in to RTC. Must supply this or a password file if the * credentials id was not supplied. (Job setting) * @param passwordFile File containing the password to use when logging in to RTC. Must supply this or a * password if the credentials id was not supplied. (Job setting) * @param credId Credential id that will identify the user id and password to use (Job setting) * @param timeout The timeout period for the connection (Job setting) * @param avoidUsingBuildToolkit Whether to use REST api instead of the build toolkit when testing the * connection. (Job setting) * @param processArea The name of the project or team area * @param buildTypeStr Type of the build configuration * @param buildWorkspace The workspace to build from * @param loadRules Path to the file specifying the component-to-load-rule file mapping * * @return Whether the component-to-load-rule file mapping is valid or not. Never <code>null</code> */ public FormValidation doValidateLoadRules(@AncestorInPath Job<?, ?> project, @QueryParameter("overrideGlobal") final String override, @QueryParameter("buildTool") String buildTool, @QueryParameter("serverURI") String serverURI, @QueryParameter("timeout") String timeout, @QueryParameter("userId") String userId, @QueryParameter("password") String password, @QueryParameter("passwordFile") String passwordFile, @QueryParameter("credentialsId") String credId, @QueryParameter("avoidUsingToolkit") String avoidUsingBuildToolkit, @QueryParameter("processArea") String processArea, @QueryParameter("value") String buildTypeStr, @QueryParameter("buildWorkspace") String buildWorkspace, @QueryParameter("buildStream") String buildStream, @QueryParameter("loadRules") String loadRules) { LOGGER.finest("DescriptorImpl.doValidateLoadRules: Begin"); //$NON-NLS-1$ // first validate if the user has specified the // component-to-load-rule mapping file path if (loadRules == null || loadRules.trim().length() == 0) { return FormValidation.error(Messages.RTCScm_load_rule_mapping_specify_file_path()); } // validate if the required fields are provided // this validation does not require any server call, so it is better // to fail upright than failing after validating other details like // workspace which requires a server call String loadRulesJson = null; try { loadRulesJson = Helper.validateAndGetLoadRulesJson(loadRules); } catch (Exception e) { return FormValidation.error(e, e.getMessage()); } boolean overrideGlobalSettings = Boolean.parseBoolean(override); boolean avoidUsingToolkit; if (!overrideGlobalSettings) { // use the global settings instead of the ones supplied buildTool = getGlobalBuildTool(); serverURI = getGlobalServerURI(); credId = getGlobalCredentialsId(); userId = getGlobalUserId(); password = getGlobalPassword(); passwordFile = getGlobalPasswordFile(); timeout = Integer.toString(getGlobalTimeout()); avoidUsingToolkit = getGlobalAvoidUsingToolkit(); } else { avoidUsingToolkit = Boolean.parseBoolean(avoidUsingBuildToolkit); } // validate the info for connecting to the server (including toolkit // & auth info). ValidationResult connectInfoCheck = validateConnectInfo(project, !overrideGlobalSettings, buildTool, serverURI, userId, password, passwordFile, credId, timeout, avoidUsingToolkit); if (connectInfoCheck.validationResult.kind.equals(FormValidation.Kind.ERROR)) { return connectInfoCheck.validationResult; } // conenct info is good, now validate the component-to-load-rule // file mapping entries boolean isStreamConfiguration = BUILD_STREAM_TYPE.equals(buildTypeStr); if (isStreamConfiguration) { buildWorkspace = buildStream; } FormValidation loadRulesCheck = checkLoadRules(connectInfoCheck.buildToolkitPath, avoidUsingToolkit, connectInfoCheck.loginInfo, processArea, isStreamConfiguration, buildWorkspace, loadRulesJson); return Helper.mergeValidationResults(connectInfoCheck.validationResult, loadRulesCheck); } /** * Validate that the build workspace exists and there is just one. * This is done in the next "layer" below using either the toolkit * or the rest service. * @param buildToolkitPath Path to the build toolkit * @param avoidUsingToolkit Whether to avoid using the build toolkit * @param loginInfo The login credentials * @param buildWorkspace The name of the workspace to validate * @return The result of the validation. Never <code>null</code> */ private FormValidation checkBuildWorkspace(String buildToolkitPath, boolean avoidUsingToolkit, RTCLoginInfo loginInfo, String buildWorkspace) { try { String errorMessage = RTCFacadeFacade.testBuildWorkspace(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingToolkit, buildWorkspace); if (errorMessage != null && errorMessage.length() != 0) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildWorkspace attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildWorkspace invocation failure " + eToReport.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildWorkspace attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildWorkspace failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, e.getMessage()); } return FormValidation.ok(Messages.RTCScm_build_workspace_success()); } private FormValidation checkBuildStream(String buildToolkitPath, boolean avoidUsingToolkit, RTCLoginInfo loginInfo, String processArea, String buildStream) { try { String errorMessage = RTCFacadeFacade.testBuildStream(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingToolkit, processArea, buildStream); errorMessage = Util.fixEmptyAndTrim(errorMessage); if (errorMessage != null) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException exp){ Throwable eToReport = exp.getCause(); if (eToReport == null) { eToReport = exp; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildStream attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildStream=\"" + buildStream + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildStream invocation failure " + eToReport.getMessage(), exp); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception exp) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildStream attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildStream=\"" + buildStream + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildStream failed " + exp.getMessage(), exp); //$NON-NLS-1$ } return FormValidation.error(exp, exp.getMessage()); } return FormValidation.ok(Messages.RTCScm_build_stream_success()); } /** * Validate that the build snapshot exists and there is just one. Validation using REST service is not supported. * * @param buildToolkitPath Path to the build toolkit * @param avoidUsingToolkit Whether to avoid using the build toolkit * @param loginInfo The login credentials * @param buildSnapshotContextMap Name-Value pairs representing the snapshot owner details * @param buildWorkspace The name of the workspace to validate * @return The result of the validation. Never <code>null</code> */ private FormValidation checkBuildSnapshot(String buildToolkitPath, RTCLoginInfo loginInfo, Map<String, String> buildSnapshotContextMap, String buildSnapshot) { try { // need not have to route through RTCFacadeFacade as we don't have rest services to validate snapshot RTCFacadeWrapper facade = RTCFacadeFactory.getFacade(buildToolkitPath, null); String errorMessage = (String)facade.invoke(RTCFacadeWrapper.TEST_BUILD_SNAPSHOT, new Class[] { String.class, // serverURI String.class, // userId String.class, // password int.class, // timeout Map.class, //buildSnapshotContextMap String.class, // buildSnapshot Locale.class }, // clientLocale loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), buildSnapshotContextMap, buildSnapshot, LocaleProvider.getLocale()); errorMessage = Util.fixEmptyAndTrim(errorMessage); if (errorMessage != null) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException exp) { Throwable eToReport = exp.getCause(); if (eToReport == null) { eToReport = exp; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildSnapshot attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildSnapshot=\"" + buildSnapshot + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildSnapshot invocation failure " + eToReport.getMessage(), exp); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception exp) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildSnapshot attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" buildSnapshot=\"" + buildSnapshot + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildSnapshot failed " + exp.getMessage(), exp); //$NON-NLS-1$ } return FormValidation.error(exp, exp.getMessage()); } return FormValidation.ok(Messages.RTCScm_build_stream_success()); } /** * Validate the build definition is a H/J definition and usable * @param buildToolkitPath Path to the build toolkit * @param avoidUsingToolkit Whether to avoid using the build toolkit * @param loginInfo The login credentials * @param buildDefinition The id of the build definition to validate * @return The result of the validation. Never <code>null</code> */ private FormValidation checkBuildDefinition(String buildToolkitPath, boolean avoidUsingToolkit, RTCLoginInfo loginInfo, String buildDefinition) { try { String errorMessage = RTCFacadeFacade.testBuildDefinition(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingToolkit, buildDefinition); if (errorMessage != null && errorMessage.length() != 0) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildDefinition attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" buildDefinition=\"" + buildDefinition +"\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildDefinition invocation failure " + eToReport.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkBuildDefinition attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" buildDefinition=\"" + buildDefinition + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkBuildDefinition failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, e.getMessage()); } return FormValidation.ok(Messages.RTCScm_build_definition_success()); } /** * Validate if the specified components exist in the repository and included in the given workspace. * * @param buildToolkitPath Path to the build toolkit * @param avoidUsingToolkit Whether to avoid using the build toolkit * @param loginInfo The login credentials * @param processArea The name of the project or team area * @param isStreamConfiguration Flag that determines if the <code>buildWorkspace</code> corresponds to a workspace or stream * @param buildWorkspace The name of the workspace to validate * @param componentsToExclude Json text spe * @return The result of the validation. Never <code>null</code> */ private FormValidation checkComponentsToExclude(String buildToolkitPath, boolean avoidUsingToolkit, RTCLoginInfo loginInfo, String processArea, boolean isStreamConfiguration, String buildWorkspace, String componentsToExclude) { try { String errorMessage = RTCFacadeFacade.testComponentsToExclude(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingToolkit, processArea, isStreamConfiguration, buildWorkspace, componentsToExclude); if (errorMessage != null && errorMessage.length() != 0) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkComponentsToExclude attempted with " + //$NON-NLS-1$ "\" buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" processArea=\"" + processArea + ////$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" componentsToExclude=\"" + componentsToExclude + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkComponentsToExclude invocation failure " + eToReport.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkComponentsToExclude attempted with " + //$NON-NLS-1$ "\" buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" processArea=\"" + processArea + ////$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" componentsToExclude=\"" + componentsToExclude + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkComponentsToExclude failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, e.getMessage()); } return FormValidation.ok(Messages.RTCScm_components_to_exclude_valid()); } /** * Validate if the specified components/load rule files exist in the repository and included in the given * workspace. * * @param buildToolkitPath Path to the build toolkit * @param avoidUsingToolkit Whether to avoid using the build toolkit * @param loginInfo The login credentials * @param processArea The name of the project or team area * @param isStreamConfiguration Flag that determines if the <code>buildWorkspace</code> corresponds to a workspace or stream * @param buildWorkspace The name of the workspace to validate * @param componentsToExclude Json text spe * @return The result of the validation. Never <code>null</code> */ private FormValidation checkLoadRules(String buildToolkitPath, boolean avoidUsingToolkit, RTCLoginInfo loginInfo, String processArea, boolean isStreamConfiguration, String buildWorkspace, String loadRules) { try { String errorMessage = RTCFacadeFacade.testLoadRules(buildToolkitPath, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), avoidUsingToolkit, processArea, isStreamConfiguration, buildWorkspace, loadRules); if (errorMessage != null && errorMessage.length() != 0) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkLoadRules attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" processArea=\"" + processArea + ////$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" loadRules=\"" + loadRules + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkLoadRules invocation failure " + eToReport.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkLoadRules attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" processArea=\"" + processArea + ////$NON-NLS-1$ "\" buildWorkspace=\"" + buildWorkspace + //$NON-NLS-1$ "\" loadRules=\"" + loadRules + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkLoadRules failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, e.getMessage()); } return FormValidation.ok(Messages.RTCScm_load_rule_mapping_valid()); } /** * Validate that the project area/team area exists. Validation using REST services is not supported. * * @param buildToolkitPath Path to the build toolkit * @param loginInfo The login credentials * @param process The name of the RTC project area or team area * @return The result of the validation. Never <code>null</code> */ private FormValidation checkProcessArea(String buildToolkitPath, RTCLoginInfo loginInfo, String processArea) { try { // need not have to route through RTCFacadeFacade as we don't have rest services to validate project area RTCFacadeWrapper facade = RTCFacadeFactory.getFacade(buildToolkitPath, null); String errorMessage = (String)facade.invoke(RTCFacadeWrapper.TEST_PROCESS_AREA, new Class[] { String.class, // serverURI String.class, // userId String.class, // password int.class, // timeout String.class, // processArea Locale.class }, // clientLocale loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), processArea, LocaleProvider.getLocale()); errorMessage = Util.fixEmptyAndTrim(errorMessage); if (errorMessage != null) { return FormValidation.error(errorMessage); } } catch (InvocationTargetException e) { Throwable eToReport = e.getCause(); if (eToReport == null) { eToReport = e; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkProcessArea attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" processArea=\"" + processArea + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkProcessArea invocation failure " + eToReport.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(eToReport, Messages.RTCScm_failed_to_connect(eToReport.getMessage())); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkProcessArea attempted with " + //$NON-NLS-1$ " buildToolkitPath=\"" + buildToolkitPath + //$NON-NLS-1$ "\" serverURI=\"" + loginInfo.getServerUri() + //$NON-NLS-1$ "\" timeout=\"" + loginInfo.getTimeout() + //$NON-NLS-1$ "\" userId=\"" + loginInfo.getUserId() + //$NON-NLS-1$ "\" processArea=\"" + processArea + "\""); //$NON-NLS-1$ //$NON-NLS-2$ LOGGER.log(Level.FINER, "checkProcessArea failed " + e.getMessage(), e); //$NON-NLS-1$ } return FormValidation.error(e, e.getMessage()); } // since we do collective validation, it is ok not to return any message return FormValidation.ok(); } } /* * Convenience constructor for instantiating RTCSCM with only a buildType and the rest of the parameters are set to defaults * */ public RTCScm(BuildType buildType) { this(false, null, null, DEFAULT_SERVER_TIMEOUT, null, null, null, null, buildType, false); LOGGER.finest("RTCScm constructor 1: Begin"); } @DataBoundConstructor public RTCScm(boolean overrideGlobal, String buildTool, String serverURI, int timeout, String userId, Secret password, String passwordFile, String credentialsId, BuildType buildType, boolean avoidUsingToolkit) { LOGGER.finest("RTCScm DataBound constructor: Begin"); this.overrideGlobal = overrideGlobal; if (this.overrideGlobal) { this.buildTool = buildTool; this.serverURI = serverURI; this.timeout = timeout; this.credentialsId = credentialsId; if (this.credentialsId == null || credentialsId.isEmpty()) { this.userId = userId; this.password = password; this.passwordFile = passwordFile; } this.avoidUsingToolkit = avoidUsingToolkit; } this.buildType = buildType; if(buildType != null) { this.buildTypeStr = buildType.value; this.buildWorkspace = buildType.buildWorkspace; this.buildDefinition = buildType.buildDefinition; this.buildSnapshot = buildType.buildSnapshot; this.buildStream = buildType.buildStream; this.loadDirectory = buildType.loadDirectory; this.clearLoadDirectory = buildType.clearLoadDirectory; this.createFoldersForComponents = buildType.createFoldersForComponents; this.componentsToExclude = buildType.componentsToExclude; this.loadRules = buildType.loadRules; this.acceptBeforeLoad = buildType.acceptBeforeLoad; this.generateChangelogWithGoodBuild = buildType.generateChangelogWithGoodBuild; this.processArea = buildType.processArea; this.currentSnapshotOwnerType = buildType.currentSnapshotOwnerType; this.buildSnapshotContext = buildType.buildSnapshotContext; this.overrideDefaultSnapshotName = buildType.overrideDefaultSnapshotName; this.customizedSnapshotName = buildType.customizedSnapshotName; } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("RTCScm constructed with " + //$NON-NLS-1$ " overrideGlobal=\"" + this.overrideGlobal + //$NON-NLS-1$ "\" buildTool=\"" + this.buildTool + //$NON-NLS-1$ "\" serverURI=\"" + this.serverURI + //$NON-NLS-1$ "\" timeout=\"" + this.timeout + //$NON-NLS-1$ "\" userId=\"" + this.userId + //$NON-NLS-1$ "\" password " + (this.password == null ? "is not supplied" : "(" + Secret.toString(this.password).length() +" characters)") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ " passwordFile=\"" + this.passwordFile + //$NON-NLS-1$ "\" credentialsId=\"" + this.credentialsId + //$NON-NLS-1$ "\" buildType=\"" + this.buildTypeStr + //$NON-NLS-1$ "\" buildWorkspace=\"" + this.buildWorkspace + //$NON-NLS-1$ "\" buildDefinition=\"" + this.buildDefinition + //$NON-NLS-1$ "\" buildSnapshot=\"" + this.buildSnapshot + //$NON-NLS-1$ "\" buildStream=\"" + this.buildStream + //$NON-NLS-1$ "\" loadDirectory=\"" + this.loadDirectory + //$NON-NLS-1$ "\" clearLoadDirectory=\"" + this.clearLoadDirectory + //$NON-NLS-1$ "\" createFoldersForComponents=\"" + this.createFoldersForComponents + //$NON-NLS-1$ "\" componentsToExclude=\"" + this.componentsToExclude + //$NON-NLS-1$ "\" loadRules=\"" + this.loadRules + //$NON-NLS-1$ "\" acceptBeforeLoad=\"" + this.acceptBeforeLoad + "\" generateChangelogWithGoodBuild=\"" + this.generateChangelogWithGoodBuild); //$NON-NLS-1$ //$NON-NLS-2$ } } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @Override public SCMRevisionState calcRevisionsFromBuild(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { LOGGER.finest("RTCScm.calcRevisionsFromBuild : Begin"); // Our check for incoming changes uses the flow targets and does a real time compare // So for now we don't return a special revision state return SCMRevisionState.NONE; } /** * Expecting buildResultUUID as non-null, match with buildDefinition and buildWorkspace * @param loginInfo * @param buildResultUUID * @param buildDefinition * @param buildWorkspace * @return true if matches, false otherwise */ private boolean match(RTCLoginInfo loginInfo, String buildToolkit, String buildResultUUID, String buildDefinition, String buildWorkspace, boolean debug, TaskListener listener, Locale clientLocale) throws InterruptedException, AbortException { if (buildDefinition == null) { return false; } RTCFacadeWrapper facade; Map<String, String> details = null; try { facade = RTCFacadeFactory.getFacade(buildToolkit, debug ? listener.getLogger() : null); // get buildDefinition and buildWorkspace for buildResultUUID... details = (Map<String, String>) facade.invoke("getBuildResultUUIDDetails", new Class[] { //$NON-NLS-1$ String.class, // serverURI, String.class, // userId, String.class, // password, int.class, // timeout, String.class, // buildResultUUID, Object.class, // listener Locale.class // clientLocale }, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), buildResultUUID, listener, clientLocale); } catch (Exception e) { Throwable eToReport = e; if (eToReport instanceof InvocationTargetException && e.getCause() != null) { eToReport = e.getCause(); } if (eToReport instanceof InterruptedException) { listener.getLogger().println("Exception while getting buildResultUUID details"); throw (InterruptedException) eToReport; } // if (debug) { // debug("Failed to get buildResultUUID details", eToReport); //$NON-NLS-1$ // } // throw AbortException with this message throw new AbortException(e.getMessage()); } String buildDefinitionId = details.get("buildDefinitionId"); if (!buildDefinition.equals(buildDefinitionId)) { return false; } /* String buildWorkspaceName = details.get("buildWorkspaceName"); if (buildWorkspace != null) { if (!buildWorkspace.equals(buildWorkspaceName)) { return false; } } */ return true; } @Override public void checkout(Run<?, ?> build, Launcher arg1, FilePath workspacePath, TaskListener listener, File changeLogFile, SCMRevisionState baseline) throws IOException, InterruptedException { LOGGER.finest("RTCScm.checkout : Begin"); listener.getLogger().println(Messages.RTCScm_checkout_started()); String label = getLabel(build); String localBuildToolkit; String nodeBuildToolkit; String buildWorkspace = (getBuildTypeStr().equals(BUILD_WORKSPACE_TYPE)) ? Helper.parseConfigurationValue(build, null, Util.fixEmptyAndTrim(getBuildWorkspace()), listener): Util.fixEmptyAndTrim(getBuildWorkspace()); String buildDefinition = (getBuildTypeStr().equals(BUILD_DEFINITION_TYPE)) ? Helper.parseConfigurationValue(build, null, Util.fixEmptyAndTrim(getBuildDefinition()), listener): Util.fixEmptyAndTrim(getBuildDefinition()); String buildSnapshot = (getBuildTypeStr().equals(BUILD_SNAPSHOT_TYPE)) ? Helper.parseConfigurationValue(build, RTCJobProperties.RTC_BUILD_SNAPSHOT, Util.fixEmptyAndTrim(getBuildSnapshot()), listener): Util.fixEmptyAndTrim(getBuildSnapshot()); String buildStream = (getBuildTypeStr().equals(BUILD_STREAM_TYPE)) ? Helper.parseConfigurationValue(build, null, Util.fixEmptyAndTrim(getBuildStream()), listener): Util.fixEmptyAndTrim(getBuildStream()); String buildResultUUID = getBuildResultUUID(build, listener); validateInput(getBuildTypeStr(), buildSnapshot, buildStream); Node node = workspacePath.toComputer().getNode(); localBuildToolkit = getDescriptor().getMasterBuildToolkit(getBuildTool(), listener); // Get the build toolkit on the node where the checkout is happening. nodeBuildToolkit = getDescriptor().getBuildToolkit(getBuildTool(), node, listener); boolean debug = Boolean.parseBoolean(Helper.getStringBuildParameter(build, RTCJobProperties.DEBUG_PROPERTY, listener)); RTCLoginInfo loginInfo; try { loginInfo = getLoginInfo(build.getParent(), localBuildToolkit); } catch (InvalidCredentialsException e1) { throw new AbortException(e1.getMessage()); } // if buildResultUUID is not null then we need to match... if (buildResultUUID != null) { if (!match(loginInfo, localBuildToolkit, buildResultUUID, buildDefinition, buildWorkspace, debug, listener, LocaleProvider.getLocale())) { buildResultUUID = null; } } String buildType = getBuildTypeStr(); boolean useBuildDefinitionInBuild = BUILD_DEFINITION_TYPE.equals(buildType) || buildResultUUID != null; // Log in build result where the build was initiated from RTC // Because if initiated from RTC we will ignore build workspace if its a buildWorkspaceType if (buildResultUUID != null) { listener.getLogger().println(Messages.RTCScm_build_initiated_by()); } RTCBuildResultAction buildResultAction; try { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkout : " + build.getParent().getName() + " " + build.getDisplayName() + " " + node.getNodeName() + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ " Load directory=\"" + workspacePath.getRemote() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Build tool=\"" + getBuildTool() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Local Build toolkit=\"" + localBuildToolkit + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Node Build toolkit=\"" + nodeBuildToolkit + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Server URI=\"" + loginInfo.getServerUri() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Userid=\"" + loginInfo.getUserId() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " BuildType=\"" + buildType + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " ProcessArea=\"" + getProcessArea() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Build definition=\"" + buildDefinition + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Build workspace=\"" + buildWorkspace + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Build snapshot=\"" + buildSnapshot + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " useBuildDefinitionInBuild=\"" + useBuildDefinitionInBuild + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " Baseline Set name=\"" + label + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } if (workspacePath.isRemote()) { // Slaves do a lazy remote class loader. The slave's class loader will request things as needed // from the remote master. The class loader on the master is the one that knows about the hjplugin-rtc.jar // but not any of the toolkit jars. So trying to send the class (& its references) is problematic. // The hjplugin-rtc.jar won't be able to be found on the slave either from the regular class loader either // (since its on the master). So what we do is send our hjplugin-rtc.jar over to the slave to "prepopulate" // it in the class loader. This way we can create our special class loader referencing it and all the toolkit // jars. sendJarsToSlave(workspacePath); } RTCBuildResultSetupTask buildResultSetupTask = new RTCBuildResultSetupTask(build.getParent().getName() + " " + build.getDisplayName() + " " + node.getDisplayName(), //$NON-NLS-1$ //$NON-NLS-2$ nodeBuildToolkit, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), useBuildDefinitionInBuild, buildDefinition, buildResultUUID, label, listener, workspacePath.isRemote(), debug, LocaleProvider.getLocale()); BuildResultInfo buildResultInfo = buildResultSetupTask.localInvocation(); if (buildResultInfo == null) { buildResultInfo = workspacePath.act(buildResultSetupTask); } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("checkout : " + build.getParent().getName() + " " + build.getDisplayName() + " " + node.getDisplayName() + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ " initial buildResultUUID=\"" + buildResultUUID + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " current buildResultUUID=\"" + buildResultInfo.getBuildResultUUID() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " scheduled=\"" + buildResultInfo.isScheduled() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " personal build=\"" + buildResultInfo.isPersonalBuild() + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ " requested by=\"" + (buildResultInfo.getRequestor() == null ? "" : buildResultInfo.getRequestor()) + "\"" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ " own build life cycle=\"" + buildResultInfo.ownLifeCycle() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } if (buildResultUUID != null) { // if build started by RTC, record the cause of the build RTCBuildCause rtcBuildCause = new RTCBuildCause(buildResultInfo); CauseAction cause = build.getAction(CauseAction.class); if (cause == null) { cause = new CauseAction(rtcBuildCause); build.addAction(cause); } else { try { cause.getCauses().add(rtcBuildCause); } catch (UnsupportedOperationException exception) { // with the changes made in Jenkins Issue 33467 in 1.655, the list of Causes returned by // CauseAction is immutable. So construct a new list with the exiting causes, append // our RTCBuildCause instance to this list and replace the CauseAction instance in the build // Post 1.2.0.0 we should always be replacing the CauseAction, instead of appending to the // existing list of Causes LOGGER.fine("RTCScm.checkout: We should have tried to modify the immutable list of Causes."); //$NON-NLS-1$ List<Cause> newCauses = new ArrayList<Cause>(cause.getCauses()); newCauses.add(rtcBuildCause); CauseAction newCauseAction = new CauseAction(newCauses); build.replaceAction(newCauseAction); } } } // now that the build has been setup, start working with the actual build result // independent of whether RTC or the plugin created it buildResultUUID = buildResultInfo.getBuildResultUUID(); // add the build result information (if any) to the build through an action // properties may later be added to this action buildResultAction = new RTCBuildResultAction(loginInfo.getServerUri(), buildResultUUID, buildResultInfo.ownLifeCycle(), this); build.addAction(buildResultAction); RemoteOutputStream changeLog = null; if (changeLogFile != null) { OutputStream changeLogStream = new FileOutputStream(changeLogFile); changeLog = new RemoteOutputStream(changeLogStream); } //get the extension provider RtcExtensionProvider extProvider = RtcExtensionProvider.getExtensionProvider(build, listener); String strCallConnectorTimeout = build.getEnvironment(listener).get(CALL_CONNECTOR_TIMEOUT_PROPERTY); if ((strCallConnectorTimeout == null) || !strCallConnectorTimeout.matches("\\d+")) { strCallConnectorTimeout = ""; } String previousSnapshotUUIDForChangeLog = null; Run<?,?> previousBuild = null; // Get previous snapshot UUID for comparison in stream case Tuple<Run<?,?>, String> previousSnapshotDetails = Helper.getSnapshotUUIDFromPreviousBuild(build, localBuildToolkit, loginInfo, getProcessArea(), buildStream, getGenerateChangelogWithGoodBuild(), LocaleProvider.getLocale()); if (previousSnapshotDetails != null) { previousBuild = previousSnapshotDetails.getFirst(); previousSnapshotUUIDForChangeLog = previousSnapshotDetails.getSecond(); } // By default for build definition, the snapshotName variable is set to #<Build Number> from the hjplugin, // the RTC plugin adds "<Build Definition Id>_" as a prefix and sets "<Build Definition Id>_#<Build Number> // as the snapshot name // For repository workspace and stream, the snapshotName is set to // "<Job Name>_#<Build Number> and used as is by the RTC plugin" String snapshotName = useBuildDefinitionInBuild ? label : build.getParent().getName() + "_" + label; // check if a custom snapshot name is configured in the job // custom snapshot name, if provided, overrides the above determined default snapshot name boolean isCustomSnapshotName = false; if (overrideDefaultSnapshotName && Util.fixEmptyAndTrim(customizedSnapshotName) != null) { // resolve any references to environment variables and build variables String resolvedSnapshotName = Helper.resolveCustomSnapshotName(build, customizedSnapshotName, listener); // make sure that the resolved value is not null and not empty // if empty or null just go with the default name computed and set earlier if (Util.fixEmptyAndTrim(resolvedSnapshotName) != null) { isCustomSnapshotName = true; snapshotName = resolvedSnapshotName; } else { listener.getLogger().println(Messages.RTCScm_empty_resolved_snapshot_name(customizedSnapshotName)); } } String parentActivityId = ""; String connectorId = ""; Map<String, String> streamData = new HashMap<String, String>(); Map<String, String> buildSnapshotContextMap = buildSnapshotContext != null ? buildSnapshotContext.getContextMap() : null; RTCAcceptTask acceptTask = new RTCAcceptTask( build.getNumber() + " " + build.getParent().getName() + " " + build.getDisplayName() + " " + node.getDisplayName(), //$NON-NLS-1$ //$NON-NLS-2$ nodeBuildToolkit, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), getProcessArea(), buildResultUUID, buildWorkspace, buildSnapshotContextMap, buildSnapshot, buildStream, isCustomSnapshotName, snapshotName, previousSnapshotUUIDForChangeLog, listener, changeLog, workspacePath.isRemote(), debug, LocaleProvider.getLocale(), strCallConnectorTimeout, getAcceptBeforeLoad(), // If you are not comparing with any snapshot, no need to put the previous build URL (previousSnapshotUUIDForChangeLog != null && previousBuild != null) ? Util.fixEmptyAndTrim(previousBuild.getUrl()): null, Helper.getTemporaryWorkspaceComment(build)); // publish in the build result links to the project and the build if (buildResultUUID != null) { String rootUrl = Hudson.getInstance().getRootUrl(); if (rootUrl != null) { rootUrl = Util.encode(rootUrl); } String projectUrl = build.getParent().getUrl(); if (projectUrl != null) { projectUrl = Util.encode(projectUrl); } String buildUrl = build.getUrl(); if (buildUrl != null) { buildUrl = Util.encode(buildUrl); } acceptTask.setLinkURLs(rootUrl, projectUrl, buildUrl); } // NOTE: // For buildStream case, we have created a temporary repository workspace. Its UUID is stored in streamData. // The temporary repository workspace will be used in load() and deleted at the end of load. If we do add any // intermediate steps between accept and load and there is some exception during those tasks (even now, // we can get problems with componentsToExclude ad loadRules), we need to ensure that the temporary workspace // is deleted. // 1) A better option is to create the temporary workspace and snapshot upfront using a separate task from checkout() // That way, if there is any exception after that, we can safely delete the workspace in finally() // 2) Create and delete workspace in accept and then use load from snapshot (that already creates and deletes a // workspace). // 3) Create a workspace in accept but delete it at the end of the build in RTCRunListener. However, if load fails // the workspace is deleted immediately. // However, deleting the temporary workspace requires build toolkit on master. Some cases, the build toolkit configured // on master is invalid. In order to account for this scenario where master buildtoolkit is invalid, the temporary // repository workspace will be deleted during checkout() itself. boolean isValidMasterBuildToolkit = validateBuildToolkitPath(localBuildToolkit); boolean shouldDeleteTemporaryWorkspace = !isValidMasterBuildToolkit; Map<String, Object> acceptResult = workspacePath.act(acceptTask); Map<String, String> buildProperties = (Map<String, String>)acceptResult.get("buildProperties"); //$NON-NLS-1$ buildResultAction.addBuildProperties(buildProperties); parentActivityId = (String)acceptResult.get("parentActivityId"); //$NON-NLS-1$ connectorId = (String)acceptResult.get("connectorId"); //$NON-NLS-1$ streamData = (Map<String, String>)acceptResult.get("buildStreamData"); //$NON-NLS-1$ // Commenting it out for 1.2.0.0 // // create componentsToExclude if needed // String componentsToExcludeJson = Helper.validateAndGetComponentsToExcludeJson(componentsToExclude); // // create loadrules if needed // String loadRulesJson = Helper.validateAndGetLoadRulesJson(loadRules); RTCLoadTask loadTask = new RTCLoadTask( build.getParent().getName() + " " + build.getDisplayName() + " " + node.getDisplayName(), //$NON-NLS-1$ //$NON-NLS-2$ nodeBuildToolkit, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), getProcessArea(), buildResultUUID, buildWorkspace, buildSnapshotContextMap, buildSnapshot, buildStream, streamData, isCustomSnapshotName, snapshotName, listener, workspacePath.isRemote(), debug, LocaleProvider.getLocale(), parentActivityId, connectorId, extProvider, clearLoadDirectory, createFoldersForComponents, null, null, getAcceptBeforeLoad(), Helper.getTemporaryWorkspaceComment(build), shouldDeleteTemporaryWorkspace); if (buildResultUUID != null) { String rootUrl = Hudson.getInstance().getRootUrl(); if (rootUrl != null) { rootUrl = Util.encode(rootUrl); } String projectUrl = build.getParent().getUrl(); if (projectUrl != null) { projectUrl = Util.encode(projectUrl); } String buildUrl = build.getUrl(); if (buildUrl != null) { buildUrl = Util.encode(buildUrl); } loadTask.setLinkURLs(rootUrl, projectUrl, buildUrl); } // build properties given by load will be added to RTCBuildResultAction Map<String, Object> loadResult = null; if (Util.fixEmptyAndTrim(loadDirectory) != null) { FilePath newWorkspacePath = workspacePath.child(loadDirectory); loadResult = newWorkspacePath.act(loadTask); } else { loadResult = workspacePath.act(loadTask); } addTemporaryWorkspaceDetailsToAction(loadResult, buildResultAction); } catch (Exception e) { Throwable eToReport = e; if (eToReport instanceof InvocationTargetException) { if (e.getCause() != null) { eToReport = e.getCause(); } } if (eToReport instanceof InterruptedException) { LOGGER.log(Level.FINER, "build interrupted " + eToReport.getMessage(), eToReport); //$NON-NLS-1$ throw (InterruptedException) eToReport; } PrintWriter writer = listener.fatalError(Messages.RTCScm_checkout_failure3(eToReport.getMessage())); if (RTCScm.unexpectedFailure(eToReport)) { eToReport.printStackTrace(writer); } LOGGER.log(Level.FINER, "Create build result failure " + eToReport.getMessage(), eToReport); //$NON-NLS-1$ // if we can't check out then we can't build it throw new AbortException(Messages.RTCScm_checkout_failure4(e.getMessage())); } finally { LOGGER.finer("RTCScm.checkout : End"); } } private void sendJarsToSlave(FilePath workspacePath) throws MalformedURLException, IOException, InterruptedException { LOGGER.finest("RTCScm.sendJarsToSlave : Begin"); VirtualChannel channel = workspacePath.getChannel(); URL facadeJarURL = RTCFacadeFactory.getFacadeJarURL(null); if (channel instanceof Channel && facadeJarURL != null) { // try to find our jar Class<?> originalClass = RTCScm.class; ClassLoader originalClassLoader = (ClassLoader) originalClass.getClassLoader(); URL [] jars = new URL[] {facadeJarURL}; boolean result = ((Channel) channel).preloadJar(originalClassLoader, jars); LOGGER.finer("Prefetch result for sending jars is " + result); //$NON-NLS-1$ //$NON-NLS-2$ } LOGGER.finest("RTCScm.sendJarsToSlave : End"); } private String getLabel(Run<?, ?> build) { // TODO if we have a build definition & build result id we should probably // follow a similar algorithm to RTC? // In the simple plugin case, generate the name from the project and the build return Messages.RTCScm_build_label(build.getNumber()); } @Override public boolean supportsPolling() { LOGGER.finest("RTCScm.supportsPolling : Begin"); return true; } @Override public boolean requiresWorkspaceForPolling() { LOGGER.finest("RTCScm.requiresWorkspaceForPolling : Begin"); return false; } @Override public PollingResult compareRemoteRevisionWith( Job<?, ?> project, Launcher launcher, FilePath workspacePath, TaskListener listener, SCMRevisionState revisionState) throws IOException, InterruptedException { // if #requiresWorkspaceForPolling is false, expect that launcher and workspacePath are null LOGGER.finest("RTCScm.compareRemoteRevisionWith : Begin"); listener.getLogger().println(Messages.RTCScm_checking_for_changes()); // Get values for jazz scm stream, jazz scm workspace and build definition from default values of // job parameters if any String buildType = getBuildTypeStr(); String buildDefinition = (BUILD_DEFINITION_TYPE.equals(buildType)) ? Helper.parseConfigurationValue(project, Util.fixEmptyAndTrim(getBuildDefinition()), listener) : Util.fixEmptyAndTrim(getBuildDefinition()); String buildWorkspace = (BUILD_WORKSPACE_TYPE.equals(buildType)) ? Helper.parseConfigurationValue(project, Util.fixEmptyAndTrim(getBuildWorkspace()), listener) : Util.fixEmptyAndTrim(getBuildWorkspace()); String buildStream = (BUILD_STREAM_TYPE.equals(buildType)) ? Helper.parseConfigurationValue(project, Util.fixEmptyAndTrim(getBuildStream()), listener): Util.fixEmptyAndTrim(getBuildStream()); // Validate that given buildType is build from stream, buildStream is non empty if (BUILD_STREAM_TYPE.equals(buildType) && buildStream == null) { throw new AbortException(Messages.RTCScm_checking_for_changes_failure(Messages.RTCScm_build_stream_empty())); } // check to see if there are incoming changes try { // If the current configuration does not support polling, return no changes if (!isConfigSupportsPolling(getBuildTypeStr())) { listener.getLogger().println(Messages.RTCScm_polling_not_supported()); LOGGER.finer("Polling is not supported for this configuration"); return new PollingResult(revisionState, new RTCRevisionState(BIGINT_ZERO), Change.NONE); } // Handle polling request for build workspace or build definition boolean bAvoidUsingToolkit = getAvoidUsingToolkit(); String masterToolkit = getDescriptor().getMasterBuildToolkit(getBuildTool(), listener); RTCLoginInfo loginInfo = getLoginInfo(project, masterToolkit); boolean useBuildDefinitionInBuild = BUILD_DEFINITION_TYPE.equals(getBuildTypeStr()); if (useBuildDefinitionInBuild && bAvoidUsingToolkit) { //if toolkit is not to be used, and //if the build is in queue then their are changes, avoid a recheck and resetting the Quiet period if(isInQueue(project)) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("The build request for the project " + project.getName() + //$NON-NLS-1$ //$NON-NLS-2$ " is already in queue, return polling result as NO_CHANGES to avoid resetting the quiet time"); //$NON-NLS-1$ } return new PollingResult(revisionState, new RTCRevisionState(BIGINT_ZERO), Change.NONE); } Boolean changesIncoming = RTCFacadeFacade.incomingChangesUsingBuildDefinitionWithREST(masterToolkit, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), buildDefinition, buildWorkspace, listener); if (changesIncoming.equals(Boolean.TRUE)) { listener.getLogger().println(Messages.RTCScm_changes_found()); // arbitrarily specify non-zero revision hash in current revision state return new PollingResult(revisionState, new RTCRevisionState(BIGINT_ONE), Change.SIGNIFICANT); } else { listener.getLogger().println(Messages.RTCScm_no_changes_found()); return new PollingResult(revisionState, new RTCRevisionState(BIGINT_ZERO), Change.NONE); } } else { // If acceptBeforeLoad is false, we should always build. if (!getAcceptBeforeLoad()) { return new PollingResult(revisionState, new RTCRevisionState(BIGINT_ZERO), Change.SIGNIFICANT); } String strIgnoreOutgoingFromBuildWorkspace = System.getProperty(IGNORE_OUTGOING_FROM_BUILD_WS_WHILE_POLLING); boolean ignoreOutgoingFromBuildWorkspace = "true".equals(strIgnoreOutgoingFromBuildWorkspace); // Get the previous snapshot for stream case String streamChangesData = Helper.getStreamChangesDataFromLastBuild(project, masterToolkit, loginInfo, getProcessArea(), buildStream, LocaleProvider.getLocale()).getSecond(); BigInteger currentRevisionHash = RTCFacadeFacade.incomingChangesUsingBuildToolkit(masterToolkit, loginInfo.getServerUri(), loginInfo.getUserId(), loginInfo.getPassword(), loginInfo.getTimeout(), getProcessArea(), useBuildDefinitionInBuild, buildDefinition, buildWorkspace, buildStream, streamChangesData, listener, ignoreOutgoingFromBuildWorkspace); LOGGER.finer("currentRevisionHash is " + currentRevisionHash.toString()); RTCRevisionState currentRevisionState = new RTCRevisionState(currentRevisionHash); Change change = null; if (isInQueue(project)) { LOGGER.finer("Project is already in QUEUE"); // get last revision hash if (revisionState instanceof RTCRevisionState) { // If current hash is not equal to previous hash, then return SIGNIFICANT change // This makes the polling reset quiet period // otherwise we will let the quiet period to expire BigInteger lastRevisionHash = ((RTCRevisionState)revisionState).getLastRevisionHash(); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("LAST REVISION STATE " + lastRevisionHash.toString()); } change = currentRevisionHash.equals(lastRevisionHash) ? Change.NONE : Change.SIGNIFICANT; } else { // we are in quiet period, and previous revisionState is not of RTCRevisionState type... // ideally we should never come here. if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("The build request for the project " + project.getName() + //$NON-NLS-1$ //$NON-NLS-2$ " is already in queue, return polling result with changes as NONE to avoid resetting the quiet time"); //$NON-NLS-1$ } change = Change.NONE; } } else { if (project.isBuilding()) { LOGGER.finer("Project is building"); RTCBuildResultAction rtcBuildResultAction = project.getLastBuild().getAction(RTCBuildResultAction.class); if ((rtcBuildResultAction != null) && (rtcBuildResultAction.getBuildProperties() != null) && "true".equals(rtcBuildResultAction.getBuildProperties().get("team_scm_acceptPhaseOver"))) { // snapshot has been created, // hence further changes would not be taken by the running build... LOGGER.finer("Accept phase is over"); change = (currentRevisionHash.equals(BIGINT_ZERO)) ? Change.NONE : Change.SIGNIFICANT; } else { // build is running, but snapshot has not been created yet, // hence any further changes can still be considered by the running build // therefore return no changes to avoid queueing another build LOGGER.finer("Cannot determine if accept phase is over. Avoiding queuing a new build."); change = Change.NONE; } } else { LOGGER.finer("Project is not in queue nor it is building"); change = (currentRevisionHash.equals(BIGINT_ZERO)) ? Change.NONE : Change.SIGNIFICANT; } } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("Change is " + change.toString()); } if (change != Change.NONE) { listener.getLogger().println(Messages.RTCScm_changes_found()); } else { listener.getLogger().println(Messages.RTCScm_no_changes_found()); } return new PollingResult(revisionState, currentRevisionState, change); } } catch (Exception e) { Throwable eToReport = e; if (eToReport instanceof InvocationTargetException) { if (e.getCause() != null) { eToReport = e.getCause(); } } if (e instanceof InterruptedException) { LOGGER.log(Level.FINER, "Checking for changes interrupted " + eToReport.getMessage(), eToReport); //$NON-NLS-1$ throw (InterruptedException) e; } PrintWriter writer = listener.fatalError(Messages.RTCScm_checking_for_changes_failure(eToReport.getMessage())); if (RTCScm.unexpectedFailure(eToReport)) { eToReport.printStackTrace(writer); } // if we can't check for changes then we can't build it throw new AbortException(Messages.RTCScm_checking_for_changes_failure2(eToReport.getMessage())); } finally { LOGGER.finer("RTCScm.compareRemoteRevisionWith : End"); } } private boolean isInQueue(Job<?, ?> project) { LOGGER.finest("RTCScm.isInQueue : Begin"); if(project instanceof AbstractProject) { return project.isInQueue(); }else if (project instanceof Task && Jenkins.getInstance().getQueue() != null) { return Jenkins.getInstance().getQueue().contains((Task)project); } else { // returning false, since there is no good way to determine if job is in queue return false; } } public static boolean unexpectedFailure(Throwable e) { // This project can not reference types defined in the -rtc project, so we // need to do some clunky testing to see if the exception is notification of // a badly configured build. In that case, we just want to report the error // but not the whole stack trace. String name = e.getClass().getSimpleName(); return !("RTCConfigurationException".equals(name) || "AuthenticationException".equals(name) || e instanceof InterruptedException || e instanceof InvalidCredentialsException ); //$NON-NLS-1$ } @Override public ChangeLogParser createChangeLogParser() { LOGGER.finest("RTCScm.createChangeLogParser : Begin"); return new RTCChangeLogParser(); } @Override public RTCRepositoryBrowser getBrowser() { LOGGER.finest("RTCScm.getBrowser : Begin"); return new RTCRepositoryBrowser(getServerURI()); } public boolean getOverrideGlobal() { return overrideGlobal; } public RTCLoginInfo getLoginInfo(Job<?, ?> job, String toolkit) throws InvalidCredentialsException { return new RTCLoginInfo(job, toolkit, getServerURI(), getUserId(), getPassword(), getPasswordFile(), getCredentialsId(), getTimeout()); } public String getBuildTool() { if (!overrideGlobal) { return getDescriptor().getGlobalBuildTool(); } return buildTool; } public boolean getAvoidUsingToolkit() { if (!overrideGlobal) { return getDescriptor().getGlobalAvoidUsingToolkit(); } return avoidUsingToolkit; } public String getServerURI() { if (!overrideGlobal) { return getDescriptor().getGlobalServerURI(); } return serverURI; } public int getTimeout() { if (!overrideGlobal) { return getDescriptor().getGlobalTimeout(); } return timeout; } public String getCredentialsId() { if (!overrideGlobal) { return getDescriptor().getGlobalCredentialsId(); } return credentialsId; } public String getUserId() { if (!overrideGlobal) { return getDescriptor().getGlobalUserId(); } return userId; } public String getPassword() { String result; if (!overrideGlobal) { result = getDescriptor().getGlobalPassword(); } else if (password == null) { result = null; } else { result = password.getPlainText(); } return result; } public String getPasswordFile() { if (!overrideGlobal) { return getDescriptor().getGlobalPasswordFile(); } return passwordFile; } public File getPasswordFileFile() { String file = getPasswordFile(); if (file != null && file.length() > 0) { return new File(file); } return null; } /** * Determine if they are using the deprecated authentication in any way * Takes into account whether they are overriding the global configuration or not. * @return <code>true</code> if using user id & password or password file * <code>false</code> if using credentials or no password authentication setup. */ public boolean usingDeprecatedPassword() { if (getDescriptor().deprecatedCredentialEditAllowed()) { return true; } String credentials = getCredentialsId(); if (overrideGlobal && (credentials == null || credentials.isEmpty())) { // consider them to be using user id & old password way if supplied // not using strict validation since we would still work if too much info supplied. if (userId != null && !userId.isEmpty() && ((password != null && password.getPlainText() != null && !password.getPlainText().isEmpty()) || (passwordFile != null && !passwordFile.isEmpty()))) { return true; } } return false; } /** * Show the deprecated password file in the UI if editting allowed or its available for use * @return <code>true</code> to show it <code>false</code> otherwise */ public boolean showPasswordFile() { if (getDescriptor().deprecatedCredentialEditAllowed() || (overrideGlobal && passwordFile != null && !passwordFile.isEmpty())) { return true; } return false; } public boolean showPassword() { if (getDescriptor().deprecatedCredentialEditAllowed() || (overrideGlobal && password != null && password.getPlainText() != null && !password.getPlainText().isEmpty())) { return true; } return false; } @Exported public String getBuildTypeStr() { // migrate existing jobs with only build workspace or build definition defined boolean oldData = false; if (buildTypeStr == null && buildWorkspace != null) { buildTypeStr = BUILD_WORKSPACE_TYPE; oldData = true; }else if(buildTypeStr == null && buildDefinition != null) { buildTypeStr = BUILD_DEFINITION_TYPE; oldData = true; } if(oldData) { LOGGER.warning("The job's config.xml has data stored in an older format, to migrate to the newer format resave the job's configuration"); } return buildTypeStr; } public BuildType getBuildType() { return buildType; } public String getBuildWorkspace() { return buildWorkspace; } public String getLoadDirectory() { return loadDirectory; } public boolean getClearLoadDirectory() { return clearLoadDirectory; } public String getBuildSnapshot() { return buildSnapshot; } public String getBuildStream() { return buildStream; } public boolean getCreateFoldersForComponents() { return createFoldersForComponents; } public String getComponentsToExclude() { return componentsToExclude; } public String getLoadRules() { return loadRules; } public boolean getAcceptBeforeLoad() { // return true if the options has not been set. return !"false".equals(acceptBeforeLoad); } @Exported public String getBuildDefinition() { return buildDefinition; } public String getProcessArea() { return processArea; } public String getCurrentSnapshotOwnerType() { return currentSnapshotOwnerType; } public BuildSnapshotContext getBuildSnapshotContext() { return buildSnapshotContext; } public boolean getOverrideDefaultSnapshotName() { return overrideDefaultSnapshotName; } public String getCustomizedSnapshotName() { return customizedSnapshotName; } @Override @Exported public String getType() { return super.getType(); } public boolean getGenerateChangelogWithGoodBuild() { return generateChangelogWithGoodBuild; } @Override public String getKey() { LOGGER.finest("RTCScm.getKey : Begin"); StringBuilder key = new StringBuilder(); key.append("teamconcert-") .append(getServerURI()) .append("-") .append(getBuildTool()); if (buildDefinition != null) { key.append("-") .append(buildDefinition); } if (buildWorkspace != null) { key.append("-") .append(buildWorkspace); } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("RTCScm.getKey key is " + key); } LOGGER.finest("RTCScm.getKey : End"); return key.toString(); } private static String getBuildResultUUID(Run<?,?> build, TaskListener listener) throws IOException, InterruptedException { LOGGER.finest("RTCScm.getBuildResultUUID : Begin"); return Helper.getStringBuildParameter(build, RTCJobProperties.BUILD_RESULT_UUID, listener); } private boolean isConfigSupportsPolling(String buildType) { if (buildType.equals(BUILD_DEFINITION_TYPE) || buildType.equals(BUILD_WORKSPACE_TYPE) || buildType.equals(BUILD_STREAM_TYPE)) { return true; } return false; } private static void validateInput(String buildType, String buildSnapshotNameOrUUID, String buildStreamName) throws AbortException { // If snapshot UUID is null, we cannot proceed with checkout if (BUILD_SNAPSHOT_TYPE.equals(buildType) && buildSnapshotNameOrUUID == null) { throw new AbortException(Messages.RTCScm_checkout_failure4(Messages.RTCScm_snapshot_not_provided())); } if (BUILD_STREAM_TYPE.equals(buildType) && buildStreamName == null) { throw new AbortException(Messages.RTCScm_checkout_failure4(Messages.RTCScm_stream_not_provided())); } } @SuppressWarnings("unchecked") private void addTemporaryWorkspaceDetailsToAction(Map<String, Object> loadResult, RTCBuildResultAction action) { if (loadResult == null || action == null) { return; } Map<String, String> temporaryWorkspaceProperties = (Map<String, String>) loadResult.get(RTCJobProperties.TEMPORARY_REPO_WORKSPACE_DATA); if (temporaryWorkspaceProperties != null) { action.addBuildProperties(temporaryWorkspaceProperties); } } private boolean validateBuildToolkitPath(String buildToolkitPath) { // Check whether buildtoolkit on master is valid FormValidation buildToolkitCheck = RTCBuildToolInstallation.validateBuildToolkit(false, buildToolkitPath); if (buildToolkitCheck.kind.equals(FormValidation.Kind.OK)) { return true; } return false; } }