/******************************************************************************* * Copyright (c) 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.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.SubMonitor; import com.ibm.team.process.common.IProcessArea; import com.ibm.team.process.common.IProjectArea; import com.ibm.team.repository.client.ITeamRepository; import com.ibm.team.repository.client.internal.ItemManager; import com.ibm.team.repository.common.IItemHandle; import com.ibm.team.repository.common.TeamRepositoryException; import com.ibm.team.repository.common.UUID; import com.ibm.team.scm.client.SCMPlatform; import com.ibm.team.scm.common.IBaselineSet; import com.ibm.team.scm.common.IBaselineSetHandle; import com.ibm.team.scm.common.dto.IBaselineSetSearchCriteria; /** * Provides utility methods to retrieve information about a RTC SCM snapshot */ public final class RTCSnapshotUtils { private static final Logger LOGGER = Logger.getLogger(RTCSnapshotUtils.class.getName()); // constants for the possible values for snapshot owner type public static final String SNAPSHOT_OWNER_TYPE_STREAM = "stream"; //$NON-NLS-1$ public static final String SNAPSHOT_OWNER_TYPE_WORKSPACE = "workspace"; //$NON-NLS-1$ public static final String SNAPSHOT_OWNER_TYPE_NONE = "- none -"; //$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$ /** * Class defining the snapshot context configuration data. * * 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 class BuildSnapshotContext { public String snapshotOwnerType; public String processAreaOfOwningStream; public String owningStream; public String owningWorkspace; public BuildSnapshotContext(Map<String, String> buildSnapshotContextMap) { if (buildSnapshotContextMap != null) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("BuildSnapshotContext: Configured snapshotOwnerType: " + buildSnapshotContextMap.get(SNAPSHOT_OWNER_TYPE_KEY)); //$NON-NLS-1$ LOGGER.finest("BuildSnapshotContext: Configured processAreaOfOwningStream: " + buildSnapshotContextMap.get(PROCESS_AREA_OF_OWNING_STREAM_KEY)); //$NON-NLS-1$ LOGGER.finest("BuildSnapshotContext: Configured owningStream: " + buildSnapshotContextMap.get(OWNING_STREAM_KEY)); //$NON-NLS-1$ LOGGER.finest("BuildSnapshotContext: Configured owningWorkspace: " + buildSnapshotContextMap.get(OWNING_WORKSPACE_KEY)); //$NON-NLS-1$ } this.snapshotOwnerType = Utils.fixEmptyAndTrim(buildSnapshotContextMap.get(SNAPSHOT_OWNER_TYPE_KEY)); this.processAreaOfOwningStream = Utils.fixEmptyAndTrim(buildSnapshotContextMap.get(PROCESS_AREA_OF_OWNING_STREAM_KEY)); this.owningStream = Utils.fixEmptyAndTrim(buildSnapshotContextMap.get(OWNING_STREAM_KEY)); this.owningWorkspace = Utils.fixEmptyAndTrim(buildSnapshotContextMap.get(OWNING_WORKSPACE_KEY)); } else { // if the context map is null, set the owner type to "none" this.snapshotOwnerType = SNAPSHOT_OWNER_TYPE_NONE; } } private BuildSnapshotContext() { this.snapshotOwnerType = SNAPSHOT_OWNER_TYPE_NONE; } public static BuildSnapshotContext fixNullReference(BuildSnapshotContext buildSnapshotContext) { if (buildSnapshotContext != null) { return buildSnapshotContext; } // null reference is converted to an object with snapshot owner "none" for easy handling in the code return new BuildSnapshotContext(); } public boolean isSnapshotOwnedByWorkspace() { return SNAPSHOT_OWNER_TYPE_WORKSPACE.equals(this.snapshotOwnerType); } public boolean isSnapshotOwnedByStream() { return SNAPSHOT_OWNER_TYPE_STREAM.equals(this.snapshotOwnerType); } } /** * Given a snapshot name, return a {@link IBaselineSet} if there is a valid snapshot. * * When a snaphot owner is provided, snapshot search is scoped depending upon the provided details. * * The snapshot owner can be set to "none", "workspace", or "stream". * * Note: Any change in the behavior of snaphot search has to be updated here as well in help-buildSnapshotContext.html * * <pre> * <ul> * <li> * When the snapshot owner is set to "none", the snapshot search is not scoped and happens across the repository. * </li> * <li> * When the snapshot owner is selected to be a repository workspace but the name of the repository workspace is not * specified, then the snapshot search is not scoped and happens across the repository. * </li> * <li> * When the snapshot owner is selected to be a stream but neither the project or team area nor the owning stream * is specified, then the snapshot search is not scoped and happens across the repository. * </li> * <li> * When the snapshot owner is selected to be a stream and only the project or team area name is specified, then the * snapshot search is scoped to the snapshots associated with all streams owned by the specified project or team area. * </li> * <li> * When the snapshot owner is selected to be a stream and only the stream name is specified, then the snapshot search * is scoped to the snapshots associated with all streams with the given name across the repository. * </li> * <li> * When the snapshot owner is selected to be a stream and both the project or team area and stream names are specified, * then the snapshot search is scoped to the snapshots associated with all streams with the given name in the specified project or team area. * </li> * </ul> * </pre> * * @param repository * @param Object containing the snapshot owner details * @param snapshotName * @param progress * @param clientLocale * @return a {@link IBaselineSet} if there is a valid snapshot or <code>null</code> * @throws Exception */ public static IBaselineSet getSnapshotByName(ITeamRepository repository, BuildSnapshotContext buildSnapshotContext, String snapshotName, IProgressMonitor progress, Locale clientLocale) throws Exception { SubMonitor monitor = SubMonitor.convert(progress, 100); IBaselineSetSearchCriteria criteria = IBaselineSetSearchCriteria.FACTORY.newInstance().setExactName(snapshotName); buildSnapshotContext = BuildSnapshotContext.fixNullReference(buildSnapshotContext); IProcessArea processArea = null; if (buildSnapshotContext.isSnapshotOwnedByWorkspace()) { // though the snapshot owner is set to "workspace", the workspace name can still be null or blank if (buildSnapshotContext.owningWorkspace != null) { monitor.setWorkRemaining(150); criteria = criteria.setOwnerWorkspaceOptional(RTCWorkspaceUtils.getInstance().getWorkspace(buildSnapshotContext.owningWorkspace, repository, monitor.newChild(50), clientLocale)); } } else if (buildSnapshotContext.isSnapshotOwnedByStream()) { // see if a project or team area is provided if (buildSnapshotContext.processAreaOfOwningStream != null) { monitor.setWorkRemaining(200); processArea = RTCWorkspaceUtils.getInstance().getProcessAreaByName(buildSnapshotContext.processAreaOfOwningStream, repository, monitor.newChild(50), clientLocale); criteria = criteria.setProcessArea(processArea); } // though the snapshot owner is set to "stream", the stream name can still be null or blank if (buildSnapshotContext.owningStream != null) { monitor.setWorkRemaining(150); criteria = criteria.setOwnerWorkspaceOptional(RTCWorkspaceUtils.getInstance().getStream( buildSnapshotContext.processAreaOfOwningStream, processArea, buildSnapshotContext.owningStream, repository, monitor.newChild(50), clientLocale)); } } monitor.setWorkRemaining(100); // Run the query @SuppressWarnings("unchecked") List<IBaselineSetHandle> baselineSetHandles = (List<IBaselineSetHandle>)SCMPlatform.getWorkspaceManager(repository).findBaselineSets( criteria, 2, monitor.newChild(100)); if (baselineSetHandles.size() > 1) { handleSnapshotNameNotUnique(buildSnapshotContext, buildSnapshotContext.processAreaOfOwningStream, processArea, snapshotName, clientLocale); } if (baselineSetHandles.size() == 0) { handleSnapshotNotFound(buildSnapshotContext, buildSnapshotContext.processAreaOfOwningStream, processArea, snapshotName, clientLocale); } return getSnapshotByUUID(repository, baselineSetHandles.get(0).getItemId(), progress, clientLocale); } private static void handleSnapshotNameNotUnique(BuildSnapshotContext buildSnapshotContext, String processAreaPath, IProcessArea processArea, String snapshotName, Locale clientLocale) throws RTCConfigurationException { if (buildSnapshotContext.isSnapshotOwnedByWorkspace() && buildSnapshotContext.owningWorkspace != null) { // snapshot owned by workspace and workspace name is specified // more than one snapshot with the given name is associated with this workspace throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_ws(snapshotName, buildSnapshotContext.owningWorkspace)); } else if (buildSnapshotContext.isSnapshotOwnedByStream() && (buildSnapshotContext.owningStream != null || buildSnapshotContext.processAreaOfOwningStream != null)) { // snapshot owned by stream and either or both the stream name and process area name is specified if (buildSnapshotContext.processAreaOfOwningStream != null) { // process area name is specified if (processArea instanceof IProjectArea) { // process area is a project area if (buildSnapshotContext.owningStream != null) { // project area and stream are specified // more than one snapshot with the given name is associated with this stream in this project // area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_st_pa(snapshotName, buildSnapshotContext.owningStream, processAreaPath)); } else { // only project area is specified and stream is not specified // more than one snapshot with the given name is associated with the streams in this project // area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_pa(snapshotName, processAreaPath)); } } else { // process area is a team area if (buildSnapshotContext.owningStream != null) { // team area and stream are specified // more than one snapshot with the given name is associated with this stream in this team area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_st_ta(snapshotName, buildSnapshotContext.owningStream, processAreaPath)); } else { // only team area is specified and stream is not specified // more than one snapshot with the given name is associated with the streams in this team area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_ta(snapshotName, processAreaPath)); } } } if (buildSnapshotContext.owningStream != null) { // stream is specified without the assoicated process area // more than one snapshot with the given name is associated with this stream throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique_st(snapshotName, buildSnapshotContext.owningStream)); } } else { // no context is specified // more than one snapshot with the given name is found in the repository throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_name_not_unique(snapshotName)); } } private static void handleSnapshotNotFound(BuildSnapshotContext buildSnapshotContext, String processAreaPath, IProcessArea processArea, String snapshotName, Locale clientLocale) throws RTCConfigurationException { if (buildSnapshotContext.isSnapshotOwnedByWorkspace() && buildSnapshotContext.owningWorkspace != null) { // snapshot owned by workspace and workspace name is specified // snapshot not associated with this workspace throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_ws(snapshotName, buildSnapshotContext.owningWorkspace)); } else if (buildSnapshotContext.isSnapshotOwnedByStream() && (buildSnapshotContext.owningStream != null || buildSnapshotContext.processAreaOfOwningStream != null)) { // snapshot owned by stream and either or both the stream name and process area name is specified if (buildSnapshotContext.processAreaOfOwningStream != null) { // process area name is specified if (processArea instanceof IProjectArea) { // process area is a project area if (buildSnapshotContext.owningStream != null) { // project area and stream are specified // snapshot not associated with this stream in this project area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_st_pa(snapshotName, buildSnapshotContext.owningStream, processAreaPath)); } else { // only project area is specified and stream is not specified // snapshot not associated with any of the streams in this project area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_pa(snapshotName, processAreaPath)); } } else { // process area is a team area if (buildSnapshotContext.owningStream != null) { // team area and stream are specified // snapshot not associated with this stream in this team area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_st_ta(snapshotName, buildSnapshotContext.owningStream, processAreaPath)); } else { // only team area is specified and stream is not specified // snapshot not associated with any of the streams in this team area throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_ta(snapshotName, processAreaPath)); } } } if (buildSnapshotContext.owningStream != null) { // stream is specified without the assoicated process area // snapshot not associated with this stream throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found_st(snapshotName, buildSnapshotContext.owningStream)); } } else { // no context is specified // snapshot not found in the repository throw new RTCConfigurationException(Messages.get(clientLocale).RTCSnapshotUtils_snapshot_not_found(snapshotName)); } } /** * Given a snapshotUUID as {@link String} , return a {@link IBaselineSet} if there is a valid snapshot * * @param repository * @param snapshotUUID * @param progress * @param clientLocale * @return a {@link IBaselineSet} if there is a valid snapshot or <code>null</code> * @throws Exception */ public static IBaselineSet getSnapshotByUUID(ITeamRepository repository, String snapshotUUID, IProgressMonitor progress, Locale clientLocale) throws TeamRepositoryException { UUID buildSnapshotUUID = UUID.valueOf(snapshotUUID); return getSnapshotByUUID(repository, buildSnapshotUUID, progress, clientLocale); } /** * Given a snapshotUUID or name, get the {@link IBaselineSet} for a valid snapshot * * @param repository * @param buildSnapshotContext Object containing the snapshot owner details. Applicable only when the name of the * the snapshot is provided. For lookup by UUID owner details are not required. * @param snapshotUUIDOrName * @param progress * @param clientLocale * @return a {@link IBaselineSet} if there is a valid snapshot <code>null</code> * @throws Exception */ public static IBaselineSet getSnapshot(ITeamRepository repository, BuildSnapshotContext buildSnapshotContext, String snapshotUUIDOrName, IProgressMonitor progress, Locale clientLocale) throws Exception { try { UUID buildSnapshotUUID = UUID.valueOf(snapshotUUIDOrName); return getSnapshotByUUID(repository, buildSnapshotUUID, progress, clientLocale); } catch (IllegalArgumentException exp) { // the argument is a name return getSnapshotByName(repository, buildSnapshotContext, snapshotUUIDOrName, progress, clientLocale); } } /** * Given a snapshot UUID as {@link UUID}, return a {@link IBaselineSet} if there is a valid snapshot * @param repository * @param snapshotUUID * @param progress * @param clientLocale * @return a {@link IBaselineSet} if there is a valid snapshot or <code>null</code> * @throws Exception */ private static IBaselineSet getSnapshotByUUID(ITeamRepository repository, UUID snapshotUUID, IProgressMonitor progress, Locale clientLocale) throws TeamRepositoryException { SubMonitor monitor = SubMonitor.convert(progress, 100); IItemHandle itemHandle = IBaselineSet.ITEM_TYPE.createItemHandle(snapshotUUID, null); IBaselineSet baselineSet = (IBaselineSet) repository.itemManager().fetchCompleteItem(itemHandle, ItemManager.REFRESH, monitor); return baselineSet; } }