/*******************************************************************************
* 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.rtc;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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 org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubMonitor;
import com.ibm.team.build.common.model.IBuildConfigurationElement;
import com.ibm.team.build.common.model.IBuildDefinitionInstance;
import com.ibm.team.build.common.model.IBuildEngine;
import com.ibm.team.build.common.model.IBuildProperty;
import com.ibm.team.build.common.model.IBuildRequest;
import com.ibm.team.build.common.model.IBuildRequestHandle;
import com.ibm.team.build.common.model.IBuildResult;
import com.ibm.team.build.common.model.IBuildResultHandle;
import com.ibm.team.build.internal.common.builddefinition.IJazzScmConfigurationElement;
import com.ibm.team.build.internal.scm.BuildWorkspaceDescriptor;
import com.ibm.team.build.internal.scm.ComponentLoadRules;
import com.ibm.team.build.internal.scm.LoadComponents;
import com.ibm.team.build.internal.scm.RepositoryManager;
import com.ibm.team.filesystem.client.ILocation;
import com.ibm.team.filesystem.client.internal.PathLocation;
import com.ibm.team.filesystem.client.operations.ILoadRule2;
import com.ibm.team.filesystem.common.IFileItemHandle;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.InternalRepositoryException;
import com.ibm.team.repository.common.ItemNotFoundException;
import com.ibm.team.repository.common.PermissionDeniedException;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.scm.client.IWorkspaceConnection;
import com.ibm.team.scm.common.IBaselineSet;
import com.ibm.team.scm.common.IComponent;
import com.ibm.team.scm.common.IComponentHandle;
import com.ibm.team.scm.common.IWorkspace;
import com.ibm.team.scm.common.IWorkspaceHandle;
@SuppressWarnings("restriction")
public class BuildConfiguration {
private static final Logger LOGGER = Logger.getLogger(BuildConfiguration.class.getName());
private static final String eol = System.getProperty("line.separator"); //$NON-NLS-1$
private static final int MAX_RETRY_COUNT = 3;
private static final long SLEEP_TIMEOUT_SECONDS = 60; // seconds
private File fetchDestinationFile;
private Path fetchDestinationPath;
private boolean deleteNeeded = false;
private boolean isPersonalBuild = false;
private boolean acceptBeforeFetch = true;
private boolean includeComponents = false;
private String loadRuleUUIDs = null;
private Collection<IComponentHandle> components = Collections.emptyList();
private boolean createFoldersForComponents = false;
private ITeamRepository teamRepository;
private String hjWorkspace;
private BuildStreamDescriptor stream;
private BuildWorkspaceDescriptor workspace;
private BuildSnapshotDescriptor snapshot;
private String snapshotName;
private Map<String, String> buildProperties = new HashMap<String, String>();
private Map<String, String> temporaryRepositoryWorkspaceProperties = new HashMap<String, String>();
private boolean shouldDeleteTemporaryWorkspace = true;
/**
* Basic build configuration. Caller should also initialize prior to calling
* the get methods.
*
* @param teamRepository The team repository that contains the build artifacts
* @param hjWorkspace The Hudson/Jenkins specified build location
*/
public BuildConfiguration(ITeamRepository teamRepository, String hjWorkspace) {
this.teamRepository = teamRepository;
this.hjWorkspace = hjWorkspace;
}
public void setCreateFoldersForComponents(boolean createFoldersForComponents) {
this.createFoldersForComponents = createFoldersForComponents;
}
/**
* Set load rules from specified json data
* @param loadRules
*/
public void setLoadRules(String loadRules) {
loadRuleUUIDs = loadRules;
}
public void setIncludeComponents(boolean includeComponents) {
this.includeComponents = includeComponents;
}
public void setComponents(Collection<IComponentHandle> components) {
this.components = components;
}
/**
* Initialize configuration that describes how to build & load.
* @param workspaceHandle The build workspace. Never <code>null</code>
* @param workspaceName Name of the build workspace. Never <code>null</code>
* @param snapshotName The name to give any snapshot created. Never <code>null</code>
* @throws IOException If anything goes wrong during the initialization
*/
@Deprecated
public void initialize(IWorkspaceHandle workspaceHandle, String workspaceName, String snapshotName) throws IOException {
LOGGER.finest("BuildConfiguration.initialize for workspaceHandle : Enter");
this.workspace = new BuildWorkspaceDescriptor(getTeamRepository(), workspaceHandle.getItemId().getUuidValue(), workspaceName);
this.snapshotName = snapshotName;
this.fetchDestinationFile = new File(hjWorkspace);
this.fetchDestinationPath = new Path(fetchDestinationFile.getCanonicalPath());
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("Building workspace: " + workspaceName + " snapshotName " + snapshotName);
}
}
/**
* Initialize a configuration that describes how to load from a RTC SCM snapshot
* @param baselineSetHandle
* @param contributorHandle
* @param workspacePrefix
* @param monitor
* @throws Exception
*/
public void initialize(IBaselineSet baselineSet, IContributorHandle contributorHandle, String workspacePrefix, String workspaceComment,
IConsoleOutput listener, Locale clientLocale, IProgressMonitor monitor) throws Exception {
LOGGER.finest("BuildConfiguration.initialize for baselineSetHandle : Enter");
SubMonitor progress = SubMonitor.convert(monitor, 10);
IWorkspaceConnection workspaceConnection = null;
String workspaceName = workspacePrefix + "_" + Long.toString(System.currentTimeMillis()) + "_" + Long.toString(System.nanoTime());
try {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finest("BuildConfiguration.initialize for baselineSetHandle : Creating workspace '" + workspaceName + "'");
}
workspaceConnection = createRepositoryWorkspaceWithRetry(getTeamRepository(), baselineSet, workspaceName,
workspaceComment, contributorHandle, listener, progress.newChild(5));
String snapshotUUID = baselineSet.getItemId().getUuidValue();
this.snapshot = new BuildSnapshotDescriptor(teamRepository, snapshotUUID, baselineSet);
this.workspace = new BuildWorkspaceDescriptor(teamRepository,
workspaceConnection.getResolvedWorkspace().getItemId().getUuidValue(),
workspaceName);
this.fetchDestinationFile = new File(hjWorkspace);
this.fetchDestinationPath = new Path(fetchDestinationFile.getCanonicalPath());
this.acceptBeforeFetch = false;
this.isPersonalBuild = false;
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("Loading from snapshot: " + snapshotUUID + " using temporary workspace '" + workspaceName + "'");
}
} catch (Exception exp) {
if (workspaceConnection != null) {
getRTCWorkspaceUtils().deleteSilent(workspaceConnection.getResolvedWorkspace(),
getTeamRepository(), progress, listener, clientLocale);
}
throw exp;
}
}
/**
* Initialize configuration that describes how to build & load with a RTC SCM workspace
* @param workspaceHandle The build workspace. Never <code>null</code>
* @param workspaceName Name of the build workspace. Never <code>null</code>
* @param snapshotName The name to give any snapshot created. Never <code>null</code>
* @param acceptBeforeLoad Accept latest changes before loading if true
* @throws IOException If anything goes wrong during the initialization
*/
public void initialize(IWorkspaceHandle workspaceHandle, String workspaceName, String snapshotName, boolean acceptBeforeLoad) throws IOException {
LOGGER.finest("BuildConfiguration.initialize for workspaceHandle : Enter");
this.workspace = new BuildWorkspaceDescriptor(getTeamRepository(), workspaceHandle.getItemId().getUuidValue(), workspaceName);
this.snapshotName = snapshotName;
this.fetchDestinationFile = new File(hjWorkspace);
this.fetchDestinationPath = new Path(fetchDestinationFile.getCanonicalPath());
this.acceptBeforeFetch = acceptBeforeLoad;
addCommonBuildProperties();
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("Building workspace: " + workspaceName + " snapshotName " + snapshotName);
}
}
/**
* Initialize configuration that describes how to build & load with a RTC SCM stream
* @param streamHandle The stream handle. Never <code>null</code>
* @param streamName - the name of the stream
* @param snapshotName The name to give any snapshot created. Never <code>null</code>
* @param workspacePrefix Prefix for the temporary build workspace to be created. Never <code>null</code>
* @param contributorHandle The handle to the logged in contributor
* @param monitor
* @throws IOException If anything goes wrong during the initialization
* @throws TeamRepositoryException
*/
public void initialize(IWorkspaceHandle streamHandle, String streamName, IWorkspace workspace, IBaselineSet baselineSet, boolean shouldDeleteTemporaryWorkspace, IContributorHandle contributorHandle, IProgressMonitor monitor) throws IOException, TeamRepositoryException {
LOGGER.finest("BuildConfiguration.initialize for stream : Enter");
SubMonitor progress = SubMonitor.convert(monitor, 10);
try {
this.stream = new BuildStreamDescriptor(streamName, baselineSet.getItemId().getUuidValue());
this.workspace = new BuildWorkspaceDescriptor(teamRepository, workspace.getItemId().getUuidValue(), workspace.getName()) ;
this.snapshotName = baselineSet.getName();
this.fetchDestinationFile = new File(hjWorkspace);
this.fetchDestinationPath = new Path(fetchDestinationFile.getCanonicalPath());
this.acceptBeforeFetch = false;
this.isPersonalBuild = false;
this.shouldDeleteTemporaryWorkspace = shouldDeleteTemporaryWorkspace;
// Set the flow target if temporary workspace will live on after load is over
if (!shouldDeleteTemporaryWorkspace) {
// Set the flow target
getRTCWorkspaceUtils().setFlowTarget(getTeamRepository(), workspace, streamHandle, progress.newChild(5));
setTemporaryRepositoryWorkspaceProperties(workspace);
}
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("Building from stream : '" + streamName + "' with temporary workspace '" + workspace.getName() + "'. Created snapshot '" + snapshotName + "'.");
}
} finally {
progress.done();
}
}
/**
* From the given build result, get the corresponding build definition's id.
* @param teamRepository team repository instance
* @param buildResultHandle The build result that will reference the build request that contains the
* configuration details. Never <code>null</code>.
* @param snapshotName The name to give any snapshot created. Never <code>null</code>
* @param clientLocale The locale of the requesting client
* @return
* @throws TeamRepositoryException If anything goes wrong while fetching configuration
*/
public static String getBuildDefinitionId(ITeamRepository teamRepository, IBuildResultHandle buildResultHandle, IProgressMonitor progress,
Locale clientLocale) throws TeamRepositoryException {
SubMonitor monitor = SubMonitor.convert(progress, 100);
IItemManager itemManager = teamRepository.itemManager();
IBuildResult result = (IBuildResult) itemManager.fetchCompleteItem(
buildResultHandle, IItemManager.REFRESH, monitor.newChild(5));
if (result.getBuildRequests().isEmpty()) {
throw new IllegalStateException("No build request for the build result");
}
IBuildRequest buildRequest = (IBuildRequest) itemManager.fetchCompleteItem((IBuildRequestHandle) result.getBuildRequests().iterator().next(),
IItemManager.REFRESH, monitor.newChild(10));
IBuildDefinitionInstance buildDefinitionInstance = buildRequest.getBuildDefinitionInstance();
String buildDefinitionId = buildDefinitionInstance.getBuildDefinitionId();
return buildDefinitionId;
}
/**
* Initialize configuration that describes how to build & load with a build definition
* @param buildResultHandle The build result that will reference the build request that contains the
* configuration details. Never <code>null</code>. If there is no build result available, you should
* be calling {@link #initialize(IWorkspaceHandle, String, String)}
* @param isCustomSnapshotName Indicates if a custom snapshot name is configured in the Job
* @param snapshotName The name to give any snapshot created. Never <code>null</code>
* @param listener A listener that will be notified of the progress and errors encountered.
* @param progress A progress monitor to check for cancellation with (and mark progress).
* @param clientLocale The locale of the requesting client
* @throws IOException If anything is wrong with the destination location
* @throws RTCConfigurationException If validation fails
* @throws TeamRepositoryException If anything goes wrong during the initialization
*/
public void initialize(IBuildResultHandle buildResultHandle, boolean isCustomSnapshotName, String snapshotName, final IConsoleOutput listener,
IProgressMonitor progress, Locale clientLocale) throws IOException, RTCConfigurationException, TeamRepositoryException {
SubMonitor monitor = SubMonitor.convert(progress, 100);
IItemManager itemManager = getTeamRepository().itemManager();
IBuildResult result = (IBuildResult) itemManager.fetchCompleteItem(
buildResultHandle, IItemManager.REFRESH, monitor.newChild(5));
if (result.getBuildRequests().isEmpty()) {
throw new IllegalStateException("No build request for the build result");
}
IBuildRequest buildRequest = (IBuildRequest) itemManager.fetchCompleteItem((IBuildRequestHandle) result.getBuildRequests().iterator().next(),
IItemManager.REFRESH, monitor.newChild(10));
IBuildDefinitionInstance buildDefinitionInstance = buildRequest.getBuildDefinitionInstance();
IBuildConfigurationElement element = buildDefinitionInstance.getConfigurationElement(
IJazzScmConfigurationElement.ELEMENT_ID);
if (element == null) {
throw new RTCConfigurationException(Messages.get(clientLocale).BuildConfiguration_scm_not_configured(buildDefinitionInstance.getBuildDefinitionId()));
}
String workspaceUuid = getWorkspaceUuid(buildDefinitionInstance);
loadRuleUUIDs = getComponentLoadRuleUUIDs(buildDefinitionInstance);
includeComponents = getIncludeComponents(buildDefinitionInstance);
components = getLoadComponents(buildDefinitionInstance);
deleteNeeded = getDeleteBeforeFetch(buildDefinitionInstance);
createFoldersForComponents = getCreateFoldersForComponents(buildDefinitionInstance);
acceptBeforeFetch = getAcceptBeforeFetch(buildDefinitionInstance);
isPersonalBuild = result.isPersonalBuild();
if (workspaceUuid == null) {
throw new IllegalStateException("Missing build workspace specification from the build definition"); //$NON-NLS-1$
}
// If a custom snapshot name is configured in the job, hjplugin resolves any references to environment variables
// and build variables(includes the parameters) and provides the resultant snapshot name. In this case set what
// is provided.
// If a custom snapshot name is not given fallback to the default pattern - <Build Definition
// Id>_<snapshotName>, by default the snapshotName passed from hjplugin contains "#<Build_Number>" string
this.snapshotName = isCustomSnapshotName ? snapshotName : buildRequest.getBuildDefinitionInstance().getBuildDefinitionId()
+ "_" + snapshotName; //$NON-NLS-1$
workspace = new BuildWorkspaceDescriptor(getTeamRepository(), workspaceUuid, null);
// Collect Build Properties
IBuildEngine buildEngine = (IBuildEngine) itemManager.fetchCompleteItem(buildRequest.getHandler(), IItemManager.REFRESH, monitor.newChild(10));
// Add built-in properties.
// Do not add the buildResultUUID property. It will be made available in the RTCBuildResultUUID property
// We want to be clear on when it is supplied in the original env. vs. build result is created later by us.
// buildProperties.put("buildResultUUID", buildResultHandle.getItemId().getUuidValue()); //$NON-NLS-1$
buildProperties.put("requestUUID", buildRequest.getItemId().getUuidValue()); //$NON-NLS-1$
buildProperties.put("buildDefinitionId", buildRequest.getBuildDefinitionInstance().getBuildDefinitionId()); //$NON-NLS-1$
buildProperties.put("repositoryAddress", ((ITeamRepository) buildRequest.getOrigin()).getRepositoryURI()); //$NON-NLS-1$
buildProperties.put("buildEngineId", buildEngine.getId()); //$NON-NLS-1$
buildProperties.put("buildEngineHostName", getLocalHostNoException()); //$NON-NLS-1$
try {
IContributor contributor = (IContributor) itemManager.fetchCompleteItem(buildRequest.getInitiatingContributor(),
IItemManager.REFRESH, monitor.newChild(10));
buildProperties.put("buildRequesterUserId", contributor.getUserId()); //$NON-NLS-1$
} catch (ItemNotFoundException itemNotFoundException) {
// Will not add property since contributor not found
} catch (PermissionDeniedException permissionDeniedException) {
// Will not add property in case of permission denied
}
if (result.isPersonalBuild()) {
buildProperties.put("personalBuild", "true"); //$NON-NLS-1$ //$NON-NLS-2$
}
// Engine properties override built-in properties.
if (buildEngine != null) {
for (Object object : buildEngine.getProperties()) {
IBuildProperty property = (IBuildProperty) object;
buildProperties.put(property.getName(), property.getValue());
}
}
// Definition properties override engine properties.
for (Object object : buildRequest.getBuildDefinitionInstance().getProperties()) {
IBuildProperty property = (IBuildProperty) object;
buildProperties.put(property.getName(), property.getValue());
}
buildProperties.put("buildLabel", result.getLabel()); //$NON-NLS-1$
// Substitute variables in the build and config properties.
IBuildRequest workingCopy = (IBuildRequest) buildRequest.getWorkingCopy();
performPropertyVariableSubstitutions(workingCopy, buildProperties, listener);
// include the hj prefix path in the fetch destination
String fetchDestination = getFetchDestination(buildProperties, clientLocale);
fetchDestinationFile = new File(fetchDestination);
fetchDestinationPath = new Path(fetchDestinationFile.getCanonicalPath());
File workingDirectory = new File(System.getProperty("user.dir")); //$NON-NLS-1$
Path workingDirectoryPath = new Path(workingDirectory.getCanonicalPath());
if (deleteNeeded && fetchDestinationPath.isPrefixOf(workingDirectoryPath)) {
throw new RTCConfigurationException(Messages.get(clientLocale).BuildConfiguration_deleting_working_directory(
fetchDestinationFile.getCanonicalPath()));
}
if (LOGGER.isLoggable(Level.FINER)) {
StringBuilder message = new StringBuilder("Building ").append(result.getLabel()).append(eol); //$NON-NLS-1$
message.append("WorkspaceUUID ").append(workspaceUuid).append(eol); //$NON-NLS-1$
message.append("H/J workspace ").append(hjWorkspace).append(eol); //$NON-NLS-1$
message.append("fetchDestinationPath ").append(fetchDestinationPath).append(eol); //$NON-NLS-1$
message.append("deleteNeeded ").append(deleteNeeded).append(eol); //$NON-NLS-1$
message.append("isPersonalBuild ").append(isPersonalBuild).append(eol); //$NON-NLS-1$
message.append("acceptBeforeFetch ").append(acceptBeforeFetch).append(eol); //$NON-NLS-1$
message.append("number Of Components to "); //$NON-NLS-1$
if (includeComponents) {
message.append("include "); //$NON-NLS-1$
} else {
message.append("exclude "); //$NON-NLS-1$
}
message.append(components.size()).append(eol); //$NON-NLS-1$
message.append("createFoldersForComponents ").append(createFoldersForComponents).append(eol); //$NON-NLS-1$
message.append("loadRuleUUIDs ").append(loadRuleUUIDs).append(eol); //$NON-NLS-1$
message.append("snapshotName ").append(snapshotName).append(eol); //$NON-NLS-1$
message.append("buildResultUUID ").append(buildResultHandle.getItemId().getUuidValue()).append(eol); //$NON-NLS-1$
message.append("buildDefinitionId ").append(buildRequest.getBuildDefinitionInstance().getBuildDefinitionId()).append(eol); //$NON-NLS-1$
message.append("buildRequestUUID ").append(buildRequest.getItemId().getUuidValue()).append(eol); //$NON-NLS-1$
message.append("buildEngineId ").append(buildEngine.getId()).append(eol); //$NON-NLS-1$
LOGGER.finer(message.toString());
}
}
public static Map<String, String> formatAsEnvironmentVariables(Map<String, String> properties) {
Map<String, String> result = new HashMap<String, String>(properties.size());
for (Map.Entry<String, String> property : properties.entrySet()) {
String newKey = formatAsEnvironmentVariable(property.getKey());
if (newKey != null) {
result.put(newKey, property.getValue());
}
}
return result;
}
private static String formatAsEnvironmentVariable(String key) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < key.length(); i++) {
Character ch = key.charAt(i);
if (Character.isLetterOrDigit(ch)) {
sb.append(ch);
} else if (ch == '.'){
sb.append('_');
}
}
String newKey = sb.toString();
if (newKey.length() == 0 || Character.isDigit(newKey.charAt(0))) {
newKey = null;
}
return newKey;
}
/**
* @return this host name, or a fixed message if it is unknown
*/
private String getLocalHostNoException() {
String result = "{cannot determine host name}";
try {
result = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
// nothing to do
}
return result;
}
private String getWorkspaceUuid(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_WORKSPACE_UUID);
if (property != null && property.getValue().length() > 0) {
return property.getValue();
}
return null;
}
private String getFetchDestination(Map<String, String> buildProperties, Locale clientLocale) throws RTCConfigurationException {
String loadDirectory = buildProperties.get(IJazzScmConfigurationElement.PROPERTY_FETCH_DESTINATION);
if (loadDirectory != null && loadDirectory.length() > 0) {
File loadDirectoryFile = new File(loadDirectory);
if (!loadDirectoryFile.isAbsolute()) {
loadDirectoryFile = new File(hjWorkspace, loadDirectory);
}
try {
String path = loadDirectoryFile.getCanonicalPath();
return path;
} catch (IOException e) {
throw new RTCConfigurationException(Messages.get(clientLocale).BuildConfiguration_invalid_fetch_destination(loadDirectoryFile.getPath(), e.getMessage()));
}
} else {
// default to the Hudson/Jenkins destination
return hjWorkspace;
}
}
private String getComponentLoadRuleUUIDs(IBuildDefinitionInstance instance) throws TeamRepositoryException {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_COMPONENT_LOAD_RULES);
if (property != null && property.getValue() != null && property.getValue().length() > 0) {
return property.getValue();
}
return null;
}
/**
* The load rules to be used during the load.
* @param workspaceConnection The workspace containing the load rules
* @param progress A progress monitor to check for cancellation with (and mark progress).
* @return Collection of load rules for the load. The return collection
* is deliberately not typed because releases later than 3.0.1.5 use ILoadRule2
* as opposed to ILoadRule. If there are no load rules, an empty collection is
* returned. Never <code>null</code>
* @throws TeamRepositoryException If anything goes wrong retrieving the load rules
*/
@SuppressWarnings("rawtypes")
public Collection getComponentLoadRules(IWorkspaceConnection workspaceConnection, Map<String, String> compLoadRules, IProgressMonitor progress, Locale clientLocale) throws Exception {
SubMonitor monitor = SubMonitor.convert(progress, 100);
if (loadRuleUUIDs != null) {
Collection loadRules = new ArrayList<Object>();
// Determine if the new format of load rules is supported and if so, will it be able to find
// the schema to validate against.
boolean useOverride = false;
try {
BuildConfiguration.class.getClassLoader().loadClass("com.ibm.team.filesystem.client.operations.ILoadRule2");
// New load rule format supported.
try {
URL schemaURL = new URL("platform:/plugin/com.ibm.team.filesystem.client/schema/LoadRule.xsd");
schemaURL.getContent();
} catch (MalformedURLException e) {
LOGGER.log(Level.FINER, e.getMessage(), e);
useOverride = true;
} catch (IOException e) {
// can't read the contents of the schema
useOverride = true;
}
} catch (ClassNotFoundException e) {
// uses old load rule support
}
Map<String, Object> customLoadRules = getCustomLoadRules(workspaceConnection, compLoadRules, clientLocale);
Map<String, Object> allLoadRules = new HashMap<String, Object>();
if (useOverride) {
ComponentLoadRules rules = new ComponentLoadRules(loadRuleUUIDs);
Map<IComponentHandle, IFileItemHandle> ruleFiles = rules.getLoadRuleFiles();
monitor.setWorkRemaining(20 * ruleFiles.size());
for (Map.Entry<IComponentHandle, IFileItemHandle> entry : ruleFiles.entrySet()) {
IComponentHandle componentHandle = entry.getKey();;
try {
Object rule = NonValidatingLoadRuleFactory.getLoadRule(workspaceConnection, componentHandle, entry.getValue(), monitor.newChild(10));
allLoadRules.put(componentHandle.getItemId().getUuidValue(), rule);
} catch (TeamRepositoryException e1) {
// Generalized to handle more than just the VersionablePermissionException
// That exception can't be handled directly because its not defined in older releases
// which would cause class loading issues.
// Lets try to build up a more informative error message.
ITeamRepository repo = (ITeamRepository) workspaceConnection.getResolvedWorkspace().getOrigin();
IContributor contributor = repo.loggedInContributor();
IComponent component = null;
try {
component = (IComponent) repo.itemManager().fetchCompleteItem(componentHandle, IItemManager.DEFAULT, monitor.newChild(10));
} catch (TeamRepositoryException e2) {
// Just throw the original error.
throw e1;
}
if (contributor != null && component != null) {
throw new TeamRepositoryException(Messages.getDefault().BuildConfiguration_load_rule_access_failed(
contributor.getUserId(), component.getName(), e1.getMessage()), e1);
} else {
throw e1;
}
}
}
}else {
ComponentLoadRules rules = new ComponentLoadRules(loadRuleUUIDs);
Map<IComponentHandle, IFileItemHandle> ruleFiles = rules.getLoadRuleFiles();
for (Map.Entry<IComponentHandle, IFileItemHandle> entry : ruleFiles.entrySet()) {
IComponentHandle componentHandle = entry.getKey();
try {
Map<IComponentHandle, IFileItemHandle> cFile = new HashMap<IComponentHandle, IFileItemHandle>(1);
cFile.put(entry.getKey(), entry.getValue());
ComponentLoadRules cRule = new ComponentLoadRules(cFile);
Collection<ILoadRule2> cRuleObj = cRule.getLoadRules(workspaceConnection, monitor.newChild(1));
if (cRuleObj != null && cRuleObj.size() == 1) {
allLoadRules.put(componentHandle.getItemId().getUuidValue(), cRuleObj.iterator().next());
}
} catch (Exception e) {
throw new TeamRepositoryException(e);
}
}
}
//consolidate the loadrules, load rules provided via extension override the pre defined ones
if (customLoadRules != null && customLoadRules.size() > 0) {
LOGGER.finest("BuildConfiguration.getComponentLoadRules: there are load rules provided through extension."); //$NON-NLS-1$
for (Map.Entry<String, Object> cRule : customLoadRules.entrySet()) {
Object rule = cRule.getValue();
if(rule instanceof ILoadRule2) {
LOGGER.finest("BuildConfiguration.getComponentLoadRules: using load rules provided through extension for component '" + cRule.getKey() + "'."); //$NON-NLS-1$ //$NON-NLS-2$
allLoadRules.put(cRule.getKey(), rule);
}
}
}
if(allLoadRules != null && allLoadRules.size() > 0) {
loadRules = allLoadRules.values();
}
return loadRules;
}else if (compLoadRules != null && compLoadRules.size() > 0) {
//no predefine load rules defined use the custom load rules
Collection<Object> loadRules = new ArrayList<Object>();
Map<String, Object> customLoadRules = getCustomLoadRules(workspaceConnection, compLoadRules, clientLocale);
if(customLoadRules != null && customLoadRules.size() > 0) {
LOGGER.finest("BuildConfiguration.getComponentLoadRules: there are load rules provided through extension."); //$NON-NLS-1$
for (Map.Entry<String, Object> cRule : customLoadRules.entrySet()) {
Object rule = cRule.getValue();
if(rule instanceof ILoadRule2) {
LOGGER.finest("BuildConfiguration.getComponentLoadRules: using load rules provided through extension for component '" + cRule.getKey() + "'."); //$NON-NLS-1$ //$NON-NLS-2$
loadRules.add(rule);
}
}
}
if(loadRules.size() > 0) {
return loadRules;
}
}
return Collections.EMPTY_LIST;
}
/**
* Finalizer for a {@link BuildConfiguration} object
*
* @param progress A progress monitor to check for cancellation with (and mark progress).
* @param listener A listener that will be notified of the progress and errors encountered.
* @param clientLocale The locale of the requesting client
*
*/
public void tearDown(RepositoryManager repositoryManager, boolean forceDelete, IProgressMonitor monitor, IConsoleOutput listener, Locale clientLocale){
LOGGER.finest("BuildConfiguration.tearDown : Enter");
SubMonitor progress = SubMonitor.convert(monitor, 5);
if (isSnapshotLoad() || isStreamLoad()) {
try {
if (forceDelete || shouldDeleteTemporaryWorkspace) {
IWorkspace workspaceToDelete = workspace.getWorkspace(repositoryManager, progress.newChild(50));
deleteWorkspace(workspaceToDelete, progress, listener, clientLocale);
}
} catch (TeamRepositoryException exp) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.warning("BuildConfiguration.tearDown : Unable to get workspace details. Log message is " + exp.getMessage());
}
}
}
}
private void deleteWorkspace(IWorkspace workspaceToDelete, IProgressMonitor monitor, IConsoleOutput listener, Locale clientLocale) {
SubMonitor progress = SubMonitor.convert(monitor, 10);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("BuildConfiguration.deleteWorkspace : Deleting temporary workspace '" + workspaceToDelete.getName() + "'");
}
if (workspaceToDelete != null) {
getRTCWorkspaceUtils().deleteSilent(workspaceToDelete,
getTeamRepository(), progress.newChild(10), listener, clientLocale);
}
}
// TODO: Check if Object can be changed to ILoadRule2
private Map<String, Object> getCustomLoadRules(IWorkspaceConnection connection, Map<String, String> compLoadRules, Locale clientLocale)
throws Exception {
Map<String, Object> lRules = new HashMap<String, Object>();
if (compLoadRules != null && compLoadRules.size() > 0) {
for (String comp : compLoadRules.keySet()) {
String lrFile = compLoadRules.get(comp);
if (lrFile == null || lrFile.trim().length() == 0) {
throw new IllegalArgumentException(Messages.get(clientLocale).BuildConfiguration_load_rule_file_path_empty_or_null(comp));
}
File fileHandle = new File(lrFile);
if (!fileHandle.exists()) {
throw new IllegalArgumentException(Messages.get(clientLocale).BuildConfiguration_load_rule_file_does_not_exist(lrFile, comp));
}
if (!fileHandle.isFile()) {
throw new IllegalArgumentException(Messages.get(clientLocale).BuildConfiguration_load_rule_file_not_file(lrFile, comp));
}
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("BuildConfiguration.getCustomLoadRules: reading load rule for component '" + comp + "' from '" + lrFile + "'."); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
}
lRules.put(comp, NonValidatingLoadRuleFactory.getLoadRule(connection, lrFile, null));
}
}
return lRules;
}
private boolean getIncludeComponents(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_INCLUDE_COMPONENTS);
if (property != null && property.getValue().length() > 0) {
return Boolean.valueOf(property.getValue());
}
return false;
}
private Collection<IComponentHandle> getLoadComponents(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_LOAD_COMPONENTS);
if (property != null && property.getValue().length() > 0) {
return new LoadComponents(getTeamRepository(), property.getValue()).getComponentHandles();
}
return Collections.EMPTY_LIST;
}
private boolean getDeleteBeforeFetch(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_DELETE_DESTINATION_BEFORE_FETCH);
if (property != null && property.getValue().length() > 0) {
return Boolean.valueOf(property.getValue());
}
return false;
}
private boolean getCreateFoldersForComponents(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_CREATE_FOLDERS_FOR_COMPONENTS);
if (property != null && property.getValue().length() > 0) {
return Boolean.valueOf(property.getValue());
}
return false;
}
private boolean getAcceptBeforeFetch(IBuildDefinitionInstance instance) {
IBuildProperty property = instance.getProperty(IJazzScmConfigurationElement.PROPERTY_ACCEPT_BEFORE_FETCH);
if (property != null && property.getValue().length() > 0) {
return Boolean.valueOf(property.getValue());
}
return false;
}
private ITeamRepository getTeamRepository() {
return teamRepository;
}
/**
* @return Descriptor of the build workspace
*/
public BuildWorkspaceDescriptor getBuildWorkspaceDescriptor() {
return workspace;
}
/**
* @return Descriptor of the build snapshot
*/
public BuildSnapshotDescriptor getBuildSnapshotDescriptor() {
return snapshot;
}
/**
* @return Descriptor of the build stream
*/
public BuildStreamDescriptor getBuildStreamDescriptor() {
return stream;
}
/**
* @return <code>true</code> if an accept is to be performed
* prior to the loading of the workspace. <code>false</code>
* otherwise
*/
public boolean acceptBeforeFetch() {
return acceptBeforeFetch;
}
/**
* @return <code>true</code> if this is a personal build, <code>false</code>
* otherwise.
*/
public boolean isPersonalBuild() {
return isPersonalBuild;
}
/**
* @return The location where the contents of the workspace should be loaded
* Never <code>null</code>
*/
public ILocation getFetchDestinationPath() {
return new PathLocation(fetchDestinationPath);
}
/**
* @return The location where the contents of the workspace should be loaded
* Never <code>null</code>
*/
public File getFetchDestinationFile() {
return fetchDestinationFile;
}
/**
* @return <code>true</code> if the contents of the fetch destination directory should be
* deleted prior to the load. <code>false</code> otherwise
*/
public boolean isDeleteNeeded() {
return deleteNeeded;
}
/**
* @return <code>true</code>> if the list of components configured are intended to be
* loaded. <code>false</code> if the list of components configured are intended to be
* excluded from the load.
*/
public boolean includeComponents() {
return includeComponents;
}
/**
* @return The components to be either included/excluded from the load. Never
* <code>null</code>
*/
public Collection<IComponentHandle> getComponents() {
return components;
}
/**
* @return <code>true</code> if the component root directory should be loaded (and named the same
* as the component), not just the contents under the component root. <code>false</code> just load
* the contents defined by the load rule, or the items directly under the component root.
*/
public boolean createFoldersForComponents() {
return createFoldersForComponents;
}
/**
* @return The name to give the snapshot created. Never <code>null</code>
*/
public String getSnapshotName() {
return snapshotName;
}
/**
* @return The uuid of the snapshot generated. May be <code>null</code>
*/
public String getSnapshotUUID() {
if (snapshot != null) {
return snapshot.getSnapshotUUID();
}
if (stream != null) {
return stream.getSnapshotUUID();
}
return null;
}
/**
* Returns the build properties.
*
* @return Map<String, String> of build properties
*/
public Map<String, String> getBuildProperties() {
return buildProperties;
}
/**
*
* @return Map<String, String> of temporary workspace details
*/
public Map<String, String> getTemporaryRepositoryWorkspaceProperties() {
return temporaryRepositoryWorkspaceProperties;
}
public boolean isSnapshotLoad() {
return (snapshot != null && workspace != null);
}
public boolean isStreamLoad() {
return (stream != null && workspace != null);
}
/**
* Substitute for variables in the build and configuration properties.
*
* @param buildRequest
* The request. Must be a working copy.
* @param buildProperties
* The collected build properties. Includes built-in, engine, and
* definition properties.
* @param listener
* A listener that will be notified of the progress and errors encountered.
*/
private void performPropertyVariableSubstitutions(IBuildRequest buildRequest, Map<String, String> buildProperties, final IConsoleOutput listener) {
List<String> substitutions = PropertyVariableHelper.substituteBuildPropertyVariables(buildProperties);
updateRequestWithSubstitutedProperties(buildRequest, buildProperties);
printSubstitutedProperties(listener, substitutions, Messages.getDefault().BuildConfiguration_substituted_build_variables());
substitutions = PropertyVariableHelper.substituteConfigurationElementPropertyVariables(
buildRequest.getBuildDefinitionInstance().getConfigurationElements(), buildProperties);
printSubstitutedProperties(listener, substitutions, Messages.getDefault().BuildConfiguration_substituted_config_variables());
}
/**
* Take any of the substituted properties that were part of the generic
* properties in the request and update them.
*
* @param buildRequest
* The request to update. Must be a working copy.
* @param buildProperties
* The substituted property name/value pairs.
*/
private void updateRequestWithSubstitutedProperties(IBuildRequest buildRequest, Map<String, String> buildProperties) {
IBuildDefinitionInstance definitionInstance = buildRequest.getBuildDefinitionInstance();
for (Map.Entry<String, String> buildProperty : buildProperties.entrySet()) {
IBuildProperty property = definitionInstance.getProperty(buildProperty.getKey());
if (property != null) {
property.setValue(buildProperty.getValue());
}
}
}
private void printSubstitutedProperties(final IConsoleOutput listener, List<String> substitutedBuildProperties, String title) {
if (!substitutedBuildProperties.isEmpty()) {
listener.log(""); //$NON-NLS-1$
listener.log(title);
for (String description : substitutedBuildProperties) {
listener.log("\t" + description); //$NON-NLS-1$
}
listener.log(""); //$NON-NLS-1$
}
}
private void setTemporaryRepositoryWorkspaceProperties(IWorkspace workspace) {
if (workspace != null) {
this.temporaryRepositoryWorkspaceProperties.put(Constants.TEMPORARY_WORKSPACE_NAME, workspace.getName());
this.temporaryRepositoryWorkspaceProperties.put(Constants.TEMPORARY_WORKSPACE_UUID, workspace.getItemId().getUuidValue());
}
}
/**
* Add Build Properties for Repository Workspace, Snapshot, Stream load type
* However, Stream and snapshot do not use BuildConfiguration during accept
* and only accept returns properties built in BuildConfiguration to export
* to environment.
* TODO One possible way to solve this is to share the BuildConfiguration created during
* accept for load, which also means we create the workspace during accept for
* snapshot type, to maintain consistency).
*
*/
private void addCommonBuildProperties() {
if (getTeamRepository() != null) {
buildProperties.put(Constants.REPOSITORY_ADDRESS, getTeamRepository().getRepositoryURI());
}
}
/**
* Creates a repository workspace with retry logic. Retries for <code>MAX_RETRY_COUNT</code>
* times until a successful Repository Workspace is created. This is intended as a workaround
* to address defect 404637, until the problem is fixed in RTC server
*
* @param repository The RTC repository to work with
* @param baselineSet The Snapshot from which a new repository workspace should be created
* @param workspaceName The name for the new repository workspace
* @param workspaceComment The description for the new repository workspace
* @param contributor The owner for the Repository workspace
* @param progress A progress monitor
* @return a {@link IWorkspaceConnection} for the newly created Repository Workspace
* @throws TeamRepositoryException only if there is an exception on all tries to create a
* Repository Workspace. If the creation of Repository Workspace is successful in at least
* one attempt, then any previous exceptions are ignored and are not logged or thrown.
* @throws RTCInternalException
*
*/
public IWorkspaceConnection createRepositoryWorkspaceWithRetry(ITeamRepository repository,
IBaselineSet baselineSet, String workspaceName,
String workspaceComment, IContributorHandle contributor,
IConsoleOutput listener,
IProgressMonitor progress) throws TeamRepositoryException, RTCInternalException {
TeamRepositoryException caughtException = null;
IWorkspaceConnection workspaceConnection = null;
SubMonitor monitor = SubMonitor.convert(progress, 10);
try {
for (int i = 0 ; i < getRetryCount(); i++) {
try {
workspaceConnection = getRTCWorkspaceUtils().createWorkspace(repository,
baselineSet, workspaceName, workspaceComment,
contributor, monitor.newChild(8));
break;
} catch (InternalRepositoryException exp) {
caughtException = exp;
} catch (TeamRepositoryException exp) {
throw exp;
}
listener.log(Messages.getDefault().BuildConfiguration_repo_workspace_retry());
try {
if (i < getRetryCount() - 1) {
Thread.sleep(getSleepTimeout());
}
} catch (InterruptedException exp ) {
// Silently ignore and continue
}
}
monitor.worked(2);
if (workspaceConnection == null) {
listener.log(Messages.getDefault().BuildConfiguration_repo_workspace_create_failed(String.valueOf(getRetryCount())));
// Return the last caught InternalRepositoryException
throw new RTCInternalException(String.format("Failed to create a Repository Workspace after %s retries", getRetryCount()), caughtException);
}
return workspaceConnection;
} finally {
monitor.done();
}
}
/**
* Supply a different timeout for testing
*
* @return the number of milliseconds to sleep
*/
protected long getSleepTimeout() {
return SLEEP_TIMEOUT_SECONDS * 1000;
}
/**
* Supply a different retry count for testing
*
* @return the number of times to retry
*/
protected int getRetryCount() {
return MAX_RETRY_COUNT;
}
/**
* Supply a mock RTCWorkspaceUtils for testing
*
* @return {@link RTCWorkspaceUtils} instance.
*
*/
protected RTCWorkspaceUtils getRTCWorkspaceUtils() {
return RTCWorkspaceUtils.getInstance();
}
}