| package org.apache.maven.scm.provider.integrity; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import com.mks.api.Command; |
| import com.mks.api.MultiValue; |
| import com.mks.api.Option; |
| import com.mks.api.response.APIException; |
| import com.mks.api.response.Field; |
| import com.mks.api.response.Response; |
| import com.mks.api.response.WorkItem; |
| import com.mks.api.response.WorkItemIterator; |
| import com.mks.api.si.SIModelTypeName; |
| |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| |
| /** |
| * This class represents a MKS Integrity Configuration Management Project |
| * <br>Provides metadata information about a Project |
| * |
| * @author <a href="mailto:cletus@mks.com">Cletus D'Souza</a> |
| * @since 1.6 |
| */ |
| public class Project |
| { |
| public static final String NORMAL_PROJECT = "Normal"; |
| |
| public static final String VARIANT_PROJECT = "Variant"; |
| |
| public static final String BUILD_PROJECT = "Build"; |
| |
| private String projectName; |
| |
| private String projectType; |
| |
| private String projectRevision; |
| |
| private String fullConfigSyntax; |
| |
| private Date lastCheckpoint; |
| |
| private APISession api; |
| |
| // Create a custom comparator to compare project members |
| public static final Comparator<Member> FILES_ORDER = new Comparator<Member>() |
| { |
| public int compare( Member cmm1, Member cmm2 ) |
| { |
| return cmm1.getMemberName().compareToIgnoreCase( cmm2.getMemberName() ); |
| } |
| }; |
| |
| /** |
| * Checks if the given value is a valid MKS Integrity Label. |
| * <br>If it's invalid, this method throws an exception providing the reason as string. |
| * |
| * @param tagName The checkpoint label name |
| * @return the error message, or null if label is valid |
| */ |
| public static void validateTag( String tagName ) |
| throws Exception |
| { |
| if ( tagName == null || tagName.length() == 0 ) |
| { |
| throw new Exception( "The checkpoint label string is empty!" ); |
| } |
| |
| char ch = tagName.charAt( 0 ); |
| if ( !( ( 'A' <= ch && ch <= 'Z' ) || ( 'a' <= ch && ch <= 'z' ) ) ) |
| { |
| throw new Exception( "The checkpoint label must start with an alpha character!" ); |
| } |
| |
| for ( char invalid : "$,.:;/\\@".toCharArray() ) |
| { |
| if ( tagName.indexOf( invalid ) >= 0 ) |
| { |
| throw new Exception( |
| "The checkpoint label may cannot contain one of the following characters: $ , . : ; / \\ @" ); |
| } |
| } |
| } |
| |
| /** |
| * Creates an instance of an Integrity SCM Project |
| * |
| * @param api MKS API Session object |
| * @param configPath Configuration path for the MKS Integrity SCM Project |
| * @throws APIException |
| */ |
| public Project( APISession api, String configPath ) |
| throws APIException |
| { |
| // Initialize our local APISession |
| this.api = api; |
| try |
| { |
| // Get the project information for this project |
| Command siProjectInfoCmd = new Command( Command.SI, "projectinfo" ); |
| siProjectInfoCmd.addOption( new Option( "project", configPath ) ); |
| api.getLogger().info( "Preparing to execute si projectinfo for " + configPath ); |
| Response infoRes = api.runCommand( siProjectInfoCmd ); |
| // Get the only work item from the response |
| WorkItem wi = infoRes.getWorkItems().next(); |
| // Get the metadata information about the project |
| Field pjNameFld = wi.getField( "projectName" ); |
| Field pjTypeFld = wi.getField( "projectType" ); |
| Field pjCfgPathFld = wi.getField( "fullConfigSyntax" ); |
| Field pjChkptFld = wi.getField( "lastCheckpoint" ); |
| |
| // Convert to our class fields |
| // First obtain the project name field |
| if ( null != pjNameFld && null != pjNameFld.getValueAsString() ) |
| { |
| projectName = pjNameFld.getValueAsString(); |
| } |
| else |
| { |
| api.getLogger().warn( "Project info did not provide a value for the 'projectName' field!" ); |
| projectName = ""; |
| } |
| // Next, we'll need to know the project type |
| if ( null != pjTypeFld && null != pjTypeFld.getValueAsString() ) |
| { |
| projectType = pjTypeFld.getValueAsString(); |
| if ( isBuild() ) |
| { |
| // Next, we'll need to know the current build checkpoint for this configuration |
| Field pjRevFld = wi.getField( "revision" ); |
| if ( null != pjRevFld && null != pjRevFld.getItem() ) |
| { |
| projectRevision = pjRevFld.getItem().getId(); |
| } |
| else |
| { |
| projectRevision = ""; |
| api.getLogger().warn( "Project info did not provide a vale for the 'revision' field!" ); |
| } |
| } |
| } |
| else |
| { |
| api.getLogger().warn( "Project info did not provide a value for the 'projectType' field!" ); |
| projectType = ""; |
| } |
| // Most important is the configuration path |
| if ( null != pjCfgPathFld && null != pjCfgPathFld.getValueAsString() ) |
| { |
| fullConfigSyntax = pjCfgPathFld.getValueAsString(); |
| } |
| else |
| { |
| api.getLogger().error( "Project info did not provide a value for the 'fullConfigSyntax' field!" ); |
| fullConfigSyntax = ""; |
| } |
| // Finally, we'll need to store the last checkpoint to figure out differences, etc. |
| if ( null != pjChkptFld && null != pjChkptFld.getDateTime() ) |
| { |
| lastCheckpoint = pjChkptFld.getDateTime(); |
| } |
| else |
| { |
| api.getLogger().warn( "Project info did not provide a value for the 'lastCheckpoint' field!" ); |
| lastCheckpoint = Calendar.getInstance().getTime(); |
| } |
| } |
| catch ( NoSuchElementException nsee ) |
| { |
| api.getLogger().error( "Project info did not provide a value for field " + nsee.getMessage() ); |
| } |
| } |
| |
| /** |
| * Returns the project path for this Integrity SCM Project |
| * |
| * @return |
| */ |
| public String getProjectName() |
| { |
| return projectName; |
| } |
| |
| /** |
| * Returns the project revision for this Integrity SCM Project |
| * |
| * @return |
| */ |
| public String getProjectRevision() |
| { |
| return projectRevision; |
| } |
| |
| /** |
| * Returns true is this is a Normal Project |
| * |
| * @return |
| */ |
| public boolean isNormal() |
| { |
| return projectType.equalsIgnoreCase( NORMAL_PROJECT ); |
| } |
| |
| /** |
| * Returns true if this is a Variant Project |
| * |
| * @return |
| */ |
| public boolean isVariant() |
| { |
| return projectType.equalsIgnoreCase( VARIANT_PROJECT ); |
| } |
| |
| /** |
| * Returns true if this is a Build Project |
| * |
| * @return |
| */ |
| public boolean isBuild() |
| { |
| return projectType.equalsIgnoreCase( BUILD_PROJECT ); |
| } |
| |
| /** |
| * Returns the Full Configuration Path for this Integrity SCM Project |
| * |
| * @return |
| */ |
| public String getConfigurationPath() |
| { |
| return fullConfigSyntax; |
| } |
| |
| /** |
| * Returns the date when the last checkpoint was performed on this Project |
| * |
| * @return |
| */ |
| public Date getLastCheckpointDate() |
| { |
| return lastCheckpoint; |
| } |
| |
| /** |
| * Parses the output from the si viewproject command to get a list of members |
| * |
| * @param workspaceDir The current workspace directory, which is required for an export |
| * @return The list of Member objects for this project |
| * @throws APIException |
| */ |
| public List<Member> listFiles( String workspaceDir ) |
| throws APIException |
| { |
| // Re-initialize the member list for this project |
| List<Member> memberList = new ArrayList<Member>(); |
| // Initialize the project config hash |
| Hashtable<String, String> pjConfigHash = new Hashtable<String, String>(); |
| // Add the mapping for this project |
| pjConfigHash.put( projectName, fullConfigSyntax ); |
| // Compute the project root directory |
| String projectRoot = projectName.substring( 0, projectName.lastIndexOf( '/' ) ); |
| |
| // Now, lets parse this project |
| Command siViewProjectCmd = new Command( Command.SI, "viewproject" ); |
| siViewProjectCmd.addOption( new Option( "recurse" ) ); |
| siViewProjectCmd.addOption( new Option( "project", fullConfigSyntax ) ); |
| MultiValue mvFields = new MultiValue( "," ); |
| mvFields.add( "name" ); |
| mvFields.add( "context" ); |
| mvFields.add( "memberrev" ); |
| mvFields.add( "membertimestamp" ); |
| mvFields.add( "memberdescription" ); |
| siViewProjectCmd.addOption( new Option( "fields", mvFields ) ); |
| api.getLogger().info( "Preparing to execute si viewproject for " + fullConfigSyntax ); |
| Response viewRes = api.runCommand( siViewProjectCmd ); |
| |
| // Iterate through the list of members returned by the API |
| WorkItemIterator wit = viewRes.getWorkItems(); |
| while ( wit.hasNext() ) |
| { |
| WorkItem wi = wit.next(); |
| if ( wi.getModelType().equals( SIModelTypeName.SI_SUBPROJECT ) ) |
| { |
| // Save the configuration path for the current subproject, using the canonical path name |
| pjConfigHash.put( wi.getField( "name" ).getValueAsString(), wi.getId() ); |
| } |
| else if ( wi.getModelType().equals( SIModelTypeName.MEMBER ) ) |
| { |
| // Figure out this member's parent project's canonical path name |
| String parentProject = wi.getField( "parent" ).getValueAsString(); |
| // Instantiate our Integrity CM Member object |
| Member iCMMember = new Member( wi, pjConfigHash.get( parentProject ), projectRoot, workspaceDir ); |
| // Add this to the full list of members in this project |
| memberList.add( iCMMember ); |
| } |
| else |
| { |
| api.getLogger().warn( "View project output contains an invalid model type: " + wi.getModelType() ); |
| } |
| } |
| |
| // Sort the files list... |
| Collections.sort( memberList, FILES_ORDER ); |
| return memberList; |
| } |
| |
| /** |
| * Performs a checkpoint on the Integrity SCM Project |
| * |
| * @param message Checkpoint description |
| * @param tag Checkpoint label |
| * @return MKS API Response object |
| * @throws APIException |
| */ |
| public Response checkpoint( String message, String tag ) |
| throws APIException |
| { |
| // Setup the checkpoint command |
| api.getLogger().debug( "Checkpointing project " + fullConfigSyntax + " with label '" + tag + "'" ); |
| // Construct the checkpoint command |
| Command siCheckpoint = new Command( Command.SI, "checkpoint" ); |
| siCheckpoint.addOption( new Option( "recurse" ) ); |
| // Set the project name |
| siCheckpoint.addOption( new Option( "project", fullConfigSyntax ) ); |
| // Set the label |
| siCheckpoint.addOption( new Option( "label", tag ) ); |
| // Set the description, if specified |
| if ( null != message && message.length() > 0 ) |
| { |
| siCheckpoint.addOption( new Option( "description", message ) ); |
| } |
| // Run the checkpoint command |
| return api.runCommand( siCheckpoint ); |
| } |
| |
| /** |
| * Creates a Development Path (project branch) for the MKS Integrity SCM Project |
| * |
| * @param devPath Development Path Name |
| * @return MKS API Response object |
| * @throws APIException |
| */ |
| public Response createDevPath( String devPath ) |
| throws APIException |
| { |
| // First we need to obtain a checkpoint from the current configuration (normal or variant) |
| String chkpt = projectRevision; |
| if ( !isBuild() ) |
| { |
| Response chkptRes = checkpoint( "Pre-checkpoint for development path " + devPath, devPath + " Baseline" ); |
| WorkItem wi = chkptRes.getWorkItem( fullConfigSyntax ); |
| chkpt = wi.getResult().getField( "resultant" ).getItem().getId(); |
| } |
| |
| // Now lets setup the create development path command |
| api.getLogger().debug( |
| "Creating development path '" + devPath + "' for project " + projectName + " at revision '" + chkpt + "'" ); |
| Command siCreateDevPath = new Command( Command.SI, "createdevpath" ); |
| siCreateDevPath.addOption( new Option( "devpath", devPath ) ); |
| // Set the project name |
| siCreateDevPath.addOption( new Option( "project", projectName ) ); |
| // Set the checkpoint we want to create the development path from |
| siCreateDevPath.addOption( new Option( "projectRevision", chkpt ) ); |
| // Run the create development path command |
| return api.runCommand( siCreateDevPath ); |
| } |
| } |
| |