Package hudson.plugins.accurev

Source Code of hudson.plugins.accurev.AccurevSCM$AccurevSCMDescriptor

package hudson.plugins.accurev;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.model.ModelObject;
import hudson.model.ParameterValue;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Hudson;
import hudson.model.ParameterDefinition;
import hudson.model.ParametersAction;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Run;
import hudson.model.StringParameterValue;
import hudson.plugins.accurev.cmd.ChangeLogCmd;
import hudson.plugins.accurev.cmd.Command;
import hudson.plugins.accurev.cmd.History;
import hudson.plugins.accurev.cmd.JustAccurev;
import hudson.plugins.accurev.cmd.Login;
import hudson.plugins.accurev.cmd.PopulateCmd;
import hudson.plugins.accurev.cmd.SetProperty;
import hudson.plugins.accurev.cmd.ShowDepots;
import hudson.plugins.accurev.cmd.ShowStreams;
import hudson.plugins.accurev.cmd.Synctime;
import hudson.plugins.jetty.security.Password;
import hudson.scm.ChangeLogParser;
import hudson.scm.ChangeLogSet;
import hudson.scm.EditType;
import hudson.scm.PollingResult;
import hudson.scm.SCMDescriptor;
import hudson.scm.SCMRevisionState;
import hudson.scm.SCM;
import hudson.util.ArgumentListBuilder;
import hudson.util.ComboBoxModel;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ListBoxModel.Option;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import net.sf.json.JSONObject;

import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

/**
* @author connollys
* @since 09-Oct-2007 16:17:34
*/
public class AccurevSCM extends SCM {

// ------------------------------ FIELDS ------------------------------

    public static final SimpleDateFormat ACCUREV_DATETIME_FORMATTER = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    @Extension
    public static final AccurevSCMDescriptor DESCRIPTOR = new AccurevSCMDescriptor();
    private static final Logger logger = Logger.getLogger(AccurevSCM.class.getName());
    static final Date NO_TRANS_DATE = new Date(0);
    private static final String DEFAULT_SNAPSHOT_NAME_FORMAT = "${JOB_NAME}_${BUILD_NUMBER}";
    private final String serverName;
    private final String depot;
    private final String stream;  
    private final boolean ignoreStreamParent;
    private final String wspaceORreftree;
    private boolean useReftree;
    private boolean useWorkspace;
    private boolean noWspaceNoReftree;
    private final boolean cleanreftree;
    private final String workspace;
    private final boolean useSnapshot;
    private final String snapshotNameFormat;
    private final boolean synctime;
    private final String reftree;
    private final String subPath;
    private final String filterForPollSCM;
    private final String directoryOffset;

// --------------------------- CONSTRUCTORS ---------------------------

    /**
     * Our constructor.
     */
    @DataBoundConstructor
    public AccurevSCM(String serverName,
                      String depot,
                      String stream,                    
                      String wspaceORreftree,
                      String workspace,                                          
                      String reftree,                     
                      String subPath,
                      String filterForPollSCM,
                      boolean synctime,
                      boolean cleanreftree, 
                      boolean useSnapshot,
                      String snapshotNameFormat,
                      String directoryOffset,
                      boolean ignoreStreamParent) {
        super();
        this.serverName = serverName;
        this.depot = depot;
        this.stream = stream;       
        this.wspaceORreftree = wspaceORreftree;
        this.workspace = workspace;       
        this.reftree = reftree;
        this.subPath = subPath;
        this.filterForPollSCM = filterForPollSCM;
        this.synctime = synctime;
        this.cleanreftree = cleanreftree;
        this.useSnapshot = useSnapshot;
        this.snapshotNameFormat = snapshotNameFormat;
        this.ignoreStreamParent = ignoreStreamParent;
        this.directoryOffset = directoryOffset;
        isWspaceORreftree();
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Getter for property 'depot'.
     *
     * @return Value for property 'depot'.
     */
    public String getDepot() {
        return depot;
    }

    /**
     * Getter for property 'serverName'.
     *
     * @return Value for property 'serverName'.
     */
    public String getServerName() {
        return serverName;
    }

    /**
     * Getter for property 'stream'.
     *
     * @return Value for property 'stream'.
     */
    public String getStream() {
        return stream;
    }
   
    /**
     * Getter for property 'wspaceORreftree'.
     *
     * @return Value for property 'wspaceORreftree'.
     */
    public String getWspaceORreftree() {     
        return wspaceORreftree;
    }
   
    /**
     * Getter for property 'reftree'.
     *
     * @return Value for property 'reftree'.
     */
    public String getReftree() {
        return reftree;
    }
   
    /**
     * Getter for property 'workspace'.
     *
     * @return Value for property 'workspace'.
     */
    public String getWorkspace() {
        return workspace;
    }

    /**
     * Getter for property 'subPath'.
     *
     * @return Value for property 'subPath'.
     */
    public String getSubPath() {
        return subPath;
    }
   
    /**
     * Getter for property 'filterForPollSCM'.
     *
     * @return Value for property 'filterForPollSCM'.
     */
    public String getFilterForPollSCM() {
        return filterForPollSCM;
    }

    /**
     * Getter for property 'snapshotNameFormat'.
     *
     * @return Value for property 'snapshotNameFormat'.
     */
    public String getSnapshotNameFormat() {
        return snapshotNameFormat;
    }

    /**
     * Getter for property 'ignoreStreamParent'.
     *
     * @return Value for property 'ignoreStreamParent'.
     */
    public boolean isIgnoreStreamParent() {
        return ignoreStreamParent;
    }

    /**
     * Getter for property 'synctime'.
     *
     * @return Value for property 'synctime'.
     */
    public boolean isSynctime() {
        return synctime;
    }

    /**
     * Getter for property 'synctime'.
     *
     * @return Value for property 'synctime'.
     */
    public void isWspaceORreftree() {
      String wspaceORreftree = getWspaceORreftree();
      if(wspaceORreftree!=null && !wspaceORreftree.isEmpty()){
        if(wspaceORreftree.equals("wspace"))
        {
          useReftree = false;
            useWorkspace = true;
            noWspaceNoReftree = false;       
        }else if(wspaceORreftree.equals("reftree")){
          useReftree = true;
            useWorkspace = false;
            noWspaceNoReftree = false;
        }else{
          useReftree = false;
            useWorkspace = false;
            noWspaceNoReftree = true;
        }
      }else{
        useReftree = false;
          useWorkspace = false;
          noWspaceNoReftree = true;
      }
     
    }
  

    /**
     * Getter for property 'cleanreftree'.
     *
     * @return Value for property 'cleanreftree'.
     */
    public boolean isCleanreftree() {
        return cleanreftree;
    }

    /**
     * Getter for property 'useSnapshot'.
     *
     * @return Value for property 'useSnapshot'.
     */
    public boolean isUseSnapshot() {
        return useSnapshot;
    }

    /**
     * Getter for property 'useRefTree'.
     *
     * @return Value for property 'useRefTree'.
     */
    public boolean isUseReftree() {
        return useReftree;
    }
   
    /**
     * Getter for property 'useWorkspace'.
     *
     * @return Value for property 'useWorkspace'.
     */
    public boolean isUseWorkspace() {
        return useWorkspace;
    }
   
    /**
     * Getter for property 'noWspaceNoReftree'.
     *
     * @return Value for property 'noWspaceNoReftree'.
     */
    public boolean isNoWspaceNoReftree() {
        return noWspaceNoReftree;
    }
   
    /**
     * Getter for property 'directoryOffset'.
     *
     * @return Value for property 'directoryOffset'.
     */
    public String getDirectoryOffset() {
        return directoryOffset;
    }

// ------------------------ INTERFACE METHODS ------------------------

// --------------------- Interface Describable ---------------------

    /**
     * {@inheritDoc}
     */
    @Override
    public SCMDescriptor<?> getDescriptor() {
        return DESCRIPTOR;
    }

// -------------------------- OTHER METHODS --------------------------

    /**
     * Exposes AccuRev-specific information to the environment.
     * The following variables become available, if not null:
     * <ul>
     <li>ACCUREV_DEPOT - The depot name</li>
     <li>ACCUREV_STREAM - The stream name</li>
     <li>ACCUREV_SERVER - The server name</li>
     <li>ACCUREV_REFTREE - The workspace name</li>
     <li>ACCUREV_SUBPATH - The workspace subpath</li>
     *
     * </ul>
     * @since 0.6.9
     */
    @Override
    public void buildEnvVars(AbstractBuild<?,?> build, Map<String, String> env) {
        // call super even though SCM.buildEnvVars currently does nothing - this could change
        super.buildEnvVars(build, env);
        AccurevServer acserver = DESCRIPTOR.getServer(serverName);
        // add various accurev-specific variables to the environment
        if (depot != null)
            env.put("ACCUREV_DEPOT", depot);
        else
          env.put("ACCUREV_DEPOT", "");
       
        if (stream != null)
            env.put("ACCUREV_STREAM", stream);
        else
          env.put("ACCUREV_STREAM", "");
       
        if (serverName != null)
            env.put("ACCUREV_SERVER", serverName);
        else
          env.put("ACCUREV_SERVER", "");
       
        if (acserver != null && acserver.getHost() != null)
          env.put("ACCUREV_SERVER_HOSTNAME", acserver.getHost());
        else
          env.put("ACCUREV_SERVER_HOSTNAME", "");
       
        if (acserver != null && acserver.getPort() > 0)
          env.put("ACCUREV_SERVER_PORT", Integer.toString(acserver.getPort()));
        else
          env.put("ACCUREV_SERVER_PORT", "");
       
        if (useWorkspace && workspace != null)
            env.put("ACCUREV_WORKSPACE", workspace);
        else
          env.put("ACCUREV_WORKSPACE", "");
       
        if (reftree != null && useReftree)
            env.put("ACCUREV_REFTREE", reftree);
        else
          env.put("ACCUREV_REFTREE", "");
       
        if (subPath != null)
            env.put("ACCUREV_SUBPATH", subPath);
        else
          env.put("ACCUREV_SUBPATH", "");
       
        // grab the last promote transaction from the changelog file
        String lastTransaction = null;
        // Abstract should have this since checkout should have already run
        ChangeLogSet<AccurevTransaction> changeSet = (ChangeLogSet<AccurevTransaction>) build.getChangeSet();
        if (!changeSet.isEmptySet()) {
            // first EDIT entry should be the last transaction we want
            for (Object o : changeSet.getItems()) {
                AccurevTransaction t = (AccurevTransaction) o;
                if (t.getEditType() == EditType.EDIT) { // this means promote or chstream in AccuRev
                   lastTransaction = t.getId();
                   break;
                }
            }
            /*
             * in case you get a changelog with no changes (e.g. a dispatch
             * message or something I don't know about yet), set something
             * different than nothing
             */
            if (lastTransaction == null) {
                lastTransaction = "NO_EDITS";
            }
        }
      if (lastTransaction != null) {
         env.put("ACCUREV_LAST_TRANSACTION", lastTransaction);
      } else {
         env.put("ACCUREV_LAST_TRANSACTION", "");
      }

      // ACCUREV_HOME is added to the build env variables
      if (System.getenv("ACCUREV_HOME") != null)
         env.put("ACCUREV_HOME", System.getenv("ACCUREV_HOME"));
    }
   
    /**
     * Starting with a given stream, walk the hierarchy back looking for the first stream with changes. Once this stream is found
     * add it's changes and any changes in its parent streams to the list of changes.
     * 
     * @param server
     * @param accurevEnv
     * @param accurevWorkingSpace
     * @param listener
     * @param accurevClientExePath
     * @param launcher
     * @param startDateOfPopulate
     * @param startTime
     * @param stream
     * @param changelogFile
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private boolean getChangesFromStreams(final AccurevServer server, final Map<String, String> accurevEnv,
          final FilePath accurevWorkingSpace, BuildListener listener, final String accurevClientExePath, Launcher launcher, final Date startDateOfPopulate,
          final Calendar startTime, AccurevStream stream, File changelogFile) throws IOException, InterruptedException {
       // There may be changes in a parent stream that we need to factor in.
       boolean foundChange = false;
       List<String> changedStreams = new ArrayList<String>();

       // Walk stream hierarchy trying to find first stream with changes...
       do {
         foundChange = CheckForChanges.checkStreamForChanges(server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher, stream,
               startTime == null ? null : startTime.getTime(), logger, this);

         if ( !foundChange ) stream = stream.getParent();
       } while (!foundChange && stream != null && stream.isReceivingChangesFromParent());

       if ( !foundChange ) return false; // No changes were found.
      
       // Found 1st stream with change, Now get changes and loop all parents
       boolean capturedChangelog = false;
       do {
           File streamChangeLog = XmlConsolidateStreamChangeLog.getStreamChangeLogFile(changelogFile, stream);
           capturedChangelog = ChangeLogCmd.captureChangelog(server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher,
                   startDateOfPopulate, startTime == null ? null : startTime.getTime(), stream.getName(), streamChangeLog, logger, this);
           if (capturedChangelog) {
               changedStreams.add(streamChangeLog.getName());
           }
           stream = stream.getParent();
       } while (stream != null && stream.isReceivingChangesFromParent() && capturedChangelog && startTime != null);
      
      XmlConsolidateStreamChangeLog.createChangeLog(changedStreams, changelogFile);
      return capturedChangelog;
    }
   
    /**
     * {@inheritDoc}
     */
    public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath jenkinsWorkspace, BuildListener listener,
                            File changelogFile) throws IOException, InterruptedException {

        final AccurevServer server = DESCRIPTOR.getServer(serverName);
        final String accurevClientExePath = jenkinsWorkspace.act(new FindAccurevClientExe(server));
        final FilePath accurevWorkingSpace = new FilePath (jenkinsWorkspace, directoryOffset == null ? "" : directoryOffset);
        final Map<String, String> accurevEnv = new HashMap<String, String>();
       
        if (!accurevWorkingSpace.exists()) accurevWorkingSpace.mkdirs();

        if (!Login.ensureLoggedInToAccurev(server, accurevEnv, jenkinsWorkspace, listener, accurevClientExePath,
                launcher)) {
            return false;
        }

        if (synctime) {
            listener.getLogger().println("Synchronizing clock with the server...");
            if (!Synctime.synctime(this, server, accurevEnv, jenkinsWorkspace, listener, accurevClientExePath, launcher)) {
                return false;
            }
        }

        if (depot == null || "".equals(depot)) {
            listener.fatalError("Must specify a depot");
            return false;
        }

        if (stream == null || "".equals(stream)) {
            listener.fatalError("Must specify a stream");
            return false;
        }

        final EnvVars environment = build.getEnvironment(listener);
        environment.put("ACCUREV_CLIENT_PATH", accurevClientExePath);

        String localStream = environment.expand(stream);

        listener.getLogger().println("Getting a list of streams...");
        final Map<String, AccurevStream> streams = ShowStreams.getStreams(this, localStream, server, accurevEnv, jenkinsWorkspace, listener, accurevClientExePath,
                launcher);

        if (streams != null && !streams.containsKey(localStream)) {
            listener.fatalError("The specified stream, '" + localStream + "' does not appear to exist!");
            return false;
        }
        if (useReftree && (this.reftree == null || "".equals(this.reftree))) {
            listener.fatalError("Must specify a reference tree");
            return false;
        }

        final Date startDateOfPopulate;
        if (useWorkspace){// _accurevWorkspace != null && _accurevWorkspace.is_useAccurevWorkspace() ) {
          AccuRevWorkspaceProcessor acWspace = new AccuRevWorkspaceProcessor(this);
           boolean result = acWspace.checkoutWorkspace( this, launcher, listener, server, accurevEnv, jenkinsWorkspace, accurevClientExePath, accurevWorkingSpace, streams, localStream );
           if (!result) return result;
           startDateOfPopulate = acWspace.get_startDateOfPopulate();
           listener.getLogger().println("Calculating latest transaction info for workspace: " + acWspace._accurevWorkspace + ".");
        } else if (useReftree) {
           AccuRevRefTreeProcessor rTree = new AccuRevRefTreeProcessor(this);
           boolean result = rTree.checkoutRefTree( this, launcher, listener, server, accurevEnv, jenkinsWorkspace, accurevClientExePath, accurevWorkingSpace, streams );
           if (!result) return result;
           startDateOfPopulate = rTree.get_startDateOfPopulate();          
        else if ( isUseSnapshot() ) {
            final String snapshotName = calculateSnapshotName(build, listener);
            listener.getLogger().println("Creating snapshot: " + snapshotName + "...");
            build.getEnvironment(listener).put("ACCUREV_SNAPSHOT", snapshotName);
            // snapshot command: accurev mksnap -H <server> -s <snapshotName> -b <backing_stream> -t now
            final ArgumentListBuilder mksnapcmd = new ArgumentListBuilder();
            mksnapcmd.add(accurevClientExePath);
            mksnapcmd.add("mksnap");
            Command.addServer(mksnapcmd, server);
            mksnapcmd.add("-s");
            mksnapcmd.add(snapshotName);
            mksnapcmd.add("-b");
            mksnapcmd.add(localStream);
            mksnapcmd.add("-t");
            mksnapcmd.add("now");
            if (!AccurevLauncher.runCommand("Create snapshot command", launcher, mksnapcmd, null, getOptionalLock(),
                    accurevEnv, jenkinsWorkspace, listener, logger, true)) {
                return false;
            }
            listener.getLogger().println("Snapshot created successfully.");
           
            PopulateCmd pop = new PopulateCmd();
            if ( pop.populate(this, launcher, listener, server, accurevClientExePath, snapshotName, true, "from workspace", accurevWorkingSpace, accurevEnv) ) {
               startDateOfPopulate = pop.get_startDateOfPopulate();
            } else {
               return false;
            }
            listener.getLogger().println("Calculating latest transaction info for stream: " + localStream + ".");
        } else {
          /*Change the background color of the stream to white as default, this background color can be optionally changed by the users to green/red upon build success/failure
             *using post build action plugins.
             */
             {
               //For AccuRev 6.0.x versions
               SetProperty.setproperty(this, accurevWorkingSpace, listener, accurevClientExePath, launcher, accurevEnv, server, localStream, "#FFFFFF", "style");
              
               //For AccuRev 6.1.x onwards
               SetProperty.setproperty(this, accurevWorkingSpace, listener, accurevClientExePath, launcher, accurevEnv, server, localStream, "#FFFFFF", "streamStyle");               
             }
           PopulateCmd pop = new PopulateCmd();
           if ( pop.populate(this, launcher, listener, server, accurevClientExePath, localStream, true, "from jenkins workspace", accurevWorkingSpace, accurevEnv) ) {
              startDateOfPopulate = pop.get_startDateOfPopulate();
           } else {
              return false;
          
           listener.getLogger().println("Calculating latest transaction info for stream: " + localStream + ".");
        }
       
       
      try {
         if (useWorkspace) {
            localStream = this.workspace;
         }
         AccurevTransaction latestTransaction = History.getLatestTransaction(this,
               server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher, localStream, null);
         if (latestTransaction == null) {
            throw new NullPointerException("The 'hist' command did not return a transaction. Does this stream have any history yet?");
         }
         String latestTransactionID = latestTransaction.getId();
         String latestTransactionDate = ACCUREV_DATETIME_FORMATTER.format(latestTransaction.getDate());
         latestTransactionDate = latestTransactionDate == null ? "1970/01/01 00:00:00" : latestTransactionDate;
         listener.getLogger().println("Latest Transaction ID: " + latestTransactionID);
         listener.getLogger().println("Latest transaction Date: " + latestTransactionDate);

         {
            environment.put("ACCUREV_LATEST_TRANSACTION_ID", latestTransactionID);
            environment.put("ACCUREV_LATEST_TRANSACTION_DATE", latestTransactionDate);

            build.addAction(new AccuRevHiddenParametersAction(environment));
         }

      } catch (Exception e) {
         listener.error("There was a problem getting the latest transaction info from the stream.");
         e.printStackTrace(listener.getLogger());
      }
       
        listener.getLogger().println(
                "Calculating changelog" + (ignoreStreamParent ? ", ignoring changes in parent" : "") + "...");

        final Calendar startTime;
        if (null == build.getPreviousBuild()) {
            listener.getLogger().println("Cannot find a previous build to compare against. Computing all changes.");
            startTime = null;
        } else {
            startTime = build.getPreviousBuild().getTimestamp();
        }
               
        AccurevStream stream = streams == null ? null : streams.get(localStream);
        if (stream == null) {
            // if there was a problem, fall back to simple stream check
            return ChangeLogCmd.captureChangelog(server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher,
                    startDateOfPopulate, startTime == null ? null : startTime.getTime(),
                    localStream, changelogFile, logger, this);
        }
       
        if (!getChangesFromStreams(server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher,
              startDateOfPopulate, startTime, stream, changelogFile)) {
           return ChangeLogCmd.captureChangelog(server, accurevEnv, accurevWorkingSpace, listener, accurevClientExePath, launcher, startDateOfPopulate,
                 startTime == null ? null : startTime.getTime(), localStream, changelogFile, logger, this);
        }
        return true;
    }
   
  private String calculateSnapshotName(final AbstractBuild<?, ?> build,
            final BuildListener listener) throws IOException, InterruptedException {
        final String actualFormat = (snapshotNameFormat == null || snapshotNameFormat
                .trim().isEmpty()) ? DEFAULT_SNAPSHOT_NAME_FORMAT : snapshotNameFormat.trim();
        final EnvVars environment = build.getEnvironment(listener);
        final String snapshotName = environment.expand(actualFormat);
        return snapshotName;
    }
   
  

    /**
     * {@inheritDoc}
     */
    public ChangeLogParser createChangeLogParser() {
        return new AccurevChangeLogParser();
    }

    private static boolean hasStringVariableReference(final String str) {
        return str != null && str.indexOf("${") != -1;
    }

    @Override
    public boolean requiresWorkspaceForPolling() {
        final boolean needSlaveForPolling = !DESCRIPTOR.isPollOnMaster();
        return needSlaveForPolling;
    }
  
    /**
     * Gets the lock to be used on "normal" accurev commands, or
     * <code>null</code> if command synchronization is switched off.
     *
     * @return See above.
     */
    public Lock getOptionalLock() {
        final AccurevServer server = DESCRIPTOR.getServer(serverName);
        final boolean shouldLock = server.isSyncOperations();
        if (shouldLock) {
            return getMandatoryLock();
        } else {
            return null;
        }
    }

    /**
     * Gets the lock to be used on accurev commands where synchronization is
     * mandatory.
     *
     * @return See above.
     */
    private Lock getMandatoryLock() {
        return AccurevSCMDescriptor.ACCUREV_LOCK;
    }

    @Override
    public SCMRevisionState calcRevisionsFromBuild(AbstractBuild<?, ?> ab, Launcher lnchr, TaskListener tl) throws IOException, InterruptedException {
        return SCMRevisionState.NONE;
    }

    @Override
    protected PollingResult compareRemoteRevisionWith(AbstractProject<?, ?> project, Launcher launcher, FilePath workspace, TaskListener listener, SCMRevisionState scmrs) throws IOException, InterruptedException {
        if( project.isInQueue()) {
            listener.getLogger().println("Project build is currently in queue.");
            return PollingResult.NO_CHANGES;
        }
        if (workspace == null) {
            // If we're claiming not to need a workspace in order to poll, then
            // workspace will be null.  In that case, we need to run directly
            // from the project folder on the master.
            final File projectDir = project.getRootDir();
            workspace = new FilePath(projectDir);
            launcher = Hudson.getInstance().createLauncher(listener);
        }
        listener.getLogger().println("Running commands from folder \""+workspace+'"');
        AccurevServer server = DESCRIPTOR.getServer(serverName);

        final String accurevPath = workspace.act(new FindAccurevClientExe(server));

        final Map<String, String> accurevEnv = new HashMap<String, String>();

        if (!Login.ensureLoggedInToAccurev(server, accurevEnv, workspace, listener, accurevPath, launcher)) {
            listener.getLogger().println("Authentication failure");
            return PollingResult.NO_CHANGES;
        }

        if (synctime) {
            listener.getLogger().println("Synchronizing clock with the server...");
            if (!Synctime.synctime(this, server, accurevEnv, workspace, listener, accurevPath, launcher)) {
                listener.getLogger().println("Synchronizing clock failure");
                return PollingResult.NO_CHANGES;
            }
        }

        final Run<?, ?> lastBuild = project.getLastBuild();
        if (lastBuild == null) {
            listener.getLogger().println("Project has never been built");
            return PollingResult.BUILD_NOW;
        }
        final Date buildDate = lastBuild.getTimestamp().getTime();

        listener.getLogger().println("Last build on " + buildDate);

        final String localStream;
        if(!useWorkspace){
        if (hasStringVariableReference(this.stream)) {
            ParametersDefinitionProperty paramDefProp = (ParametersDefinitionProperty) project
                    .getProperty(ParametersDefinitionProperty.class);

            if (paramDefProp == null) {
                listener.getLogger().println(
                        "Polling is not supported when stream name has a variable reference '" + this.stream + "'.");

                // as we don't know which stream to check we just state that
                // there is no changes
                return PollingResult.NO_CHANGES;
            }

            Map<String, String> keyValues = new TreeMap<String, String>();
   
            /* Scan for all parameter with an associated default values */
            for (ParameterDefinition paramDefinition : paramDefProp.getParameterDefinitions()) {
               
                ParameterValue defaultValue = paramDefinition.getDefaultParameterValue();

                if (defaultValue instanceof StringParameterValue) {
                    StringParameterValue strdefvalue = (StringParameterValue) defaultValue;
                    keyValues.put(defaultValue.getName(), strdefvalue.value);
                }
            }

            final EnvVars environment = new EnvVars(keyValues);
            localStream = environment.expand(this.stream);
            listener.getLogger().println("... expanded '" + this.stream + "' to '" + localStream + "'.");
        } else {
            localStream = this.stream;
        }

        if (hasStringVariableReference(localStream)) {
            listener.getLogger().println(
                    "Polling is not supported when stream name has a variable reference '" + this.stream + "'.");

            // as we don't know which stream to check we just state that there
            // is no changes
            return PollingResult.NO_CHANGES;
        }
        } else{
          localStream = this.workspace;
        }
        final Map<String, AccurevStream> streams = this.ignoreStreamParent ? null : ShowStreams.getStreams(this, localStream, server,
                accurevEnv, workspace, listener, accurevPath, launcher);
        AccurevStream stream = streams == null ? null : streams.get(localStream);

        if (stream == null) {
            // if there was a problem, fall back to simple stream check
            if (CheckForChanges.checkStreamForChanges(server, accurevEnv, workspace, listener, accurevPath, launcher, localStream,
                    buildDate,logger,this)){
                return PollingResult.BUILD_NOW;
            }else{
                return PollingResult.NO_CHANGES;
            }
        }
        // There may be changes in a parent stream that we need to factor in.
        do {
            if (CheckForChanges.checkStreamForChanges(server, accurevEnv, workspace, listener, accurevPath, launcher, stream,
                    buildDate,logger,this)) {
                return PollingResult.BUILD_NOW;
            }
            stream = stream.getParent();
        } while (stream != null && stream.isReceivingChangesFromParent());
        return PollingResult.NO_CHANGES;
    }
   
    //--------------------------- Inner Class - DescriptorImplementation ----------------------------
   
    public static final class AccurevSCMDescriptor extends SCMDescriptor<AccurevSCM> implements ModelObject {

      /**
       * The accurev server has been known to crash if more than one copy of the
       * accurev has been run concurrently on the local machine. <br>
       * Also, the accurev client has been known to complain that it's not logged
       * in if another client on the same machine logs in again.
       */
      transient static final Lock ACCUREV_LOCK = new ReentrantLock();
      private List<AccurevServer> _servers;
      // The servers field is here for backwards compatibility.
      // The transient modifier means it won't be written to the config file
      private transient List<AccurevServer> servers;

      private static final Logger descriptorlogger = Logger.getLogger(AccurevSCMDescriptor.class.getName());

      private boolean pollOnMaster; 
      private String accurevPath;

      /**
       * Constructs a new AccurevSCMDescriptor.
       */
      public AccurevSCMDescriptor() {
         super(AccurevSCM.class,null);
         load();
      }

      /**
       * {@inheritDoc}
       */
      @Override
      public String getDisplayName() {

         return "AccuRev";

      }

      /**
       * {@inheritDoc}
       */
      @Override
      public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
         this._servers = req.bindJSONToList(AccurevServer.class, formData.get("server"));
         pollOnMaster = req.getParameter("descriptor.pollOnMaster") != null;
         save();
         return true;
      }

      /**
       * {@inheritDoc}
       */
      @Override
      public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException {

         return new AccurevSCM( //
               req.getParameter("_.serverName"), //
               req.getParameter("_.depot"), //
               req.getParameter("_.stream"), //          
               req.getParameter("accurev.wspaceORreftree"),//          
               req.getParameter("accurev.workspace"),//
               req.getParameter("accurev.reftree"), //
               req.getParameter("accurev.subPath"), //
               req.getParameter("accurev.filterForPollSCM"), //
               req.getParameter("accurev.synctime") != null, //
               req.getParameter("accurev.cleanreftree") != null, //
               req.getParameter("accurev.useSnapshot") != null, //
               req.getParameter("accurev.snapshotNameFormat"), //
               req.getParameter("accurev.directoryOffset"), //
               req.getParameter("accurev.ignoreStreamParent") != null);
      }

      /**
       * Getter for property 'servers'.
       *
       * @return Value for property 'servers'.
       */
      public List<AccurevServer> getServers() {
         if (this._servers == null) {
            this._servers = new ArrayList<AccurevServer>();
         }
         // We put this here to maintain backwards compatibility
         // because we changed the name of the 'servers' field to '_servers'
         if (this.servers != null) {
            this._servers.addAll(servers);
            servers = null;
         }
         return this._servers;
      }

      /**
       * Setter for property 'servers'.
       *
       * @param servers
       *           Value to set for property 'servers'.
       */
      public void setServers(List<AccurevServer> servers) {
         this._servers = servers;
      }

      /**
       * Getter for property 'pollOnMaster'.
       *
       * @return Value for property 'pollOnMaster'.
       */
      public boolean isPollOnMaster() {
         return pollOnMaster;
      }

      /**
       * Setter for property 'pollOnMaster'.
       *
       * @param servers
       *           Value to set for property 'pollOnMaster'.
       */
      public void setPollOnMaster(boolean pollOnMaster) {
         this.pollOnMaster = pollOnMaster;
      }

      public AccurevServer getServer(String name) {
         if (name == null) {
            return null;
         }
         for (AccurevServer server : this._servers) {
            if (name.equals(server.getName())) {
               return server;
            }
         }
         return null;
      }

      // This method will populate the servers in the select box
      public ListBoxModel doFillServerNameItems(@QueryParameter String serverName) {   
       ListBoxModel s = new ListBoxModel();
       if (this._servers == null)
       {
         descriptorlogger.warning("Failed to find AccuRev server. Add Server under AccuRev section in the Manage Jenkins > Configure System page.");
           return s;
       }
           for (AccurevServer server : this._servers) {
              s.add(server.getName(), server.getName());
           }
          return s;
      }

      private static String getExistingPath(String[] paths) {
         for (final String path : paths) {
            if (new File(path).exists()) {
               return path;
            }
         }
         return "";
        
      }

      private AccurevServer getServerAndPath(String serverName) {
         final AccurevServer server = getServer(serverName);
         boolean envAccurevBin;
         String accurevBinName = "accurev";

         if (server == null) {
            return null;
         }

         String accurevBinDir = getEnvBinDir();

         if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
            // we are running on windows
            accurevBinName = "accurev.exe";
            if (!JustAccurev.justAccuRev(accurevBinDir + File.separator + accurevBinName)) {
               this.accurevPath = getExistingPath(server.getWinCmdLocations());
            } else {
               this.accurevPath = accurevBinDir + File.separator + accurevBinName;
            }
         } else {
            // we are running on *nix
            if (!JustAccurev.justAccuRev(accurevBinDir + File.separator + accurevBinName)) {
               this.accurevPath = getExistingPath(server.getNixCmdLocations());
            } else {
               this.accurevPath = accurevBinDir + File.separator + accurevBinName;
            }
         }

         if (this.accurevPath.isEmpty()) {
            // if we still don't have a path to the accurev client let's try the
            // system path
            if (JustAccurev.justAccuRev(accurevBinName)) {
               logger.info("Using the AccuRev client we found on the system path.");
               this.accurevPath = accurevBinName;
            } else {
               throw new RuntimeException("AccuRev binary is not found or not set in the environment's path.");
            }
         }

         return server;
      }

      private String getEnvBinDir() {
         String accurevBinDir = "";

         if (System.getenv("ACCUREV_BIN") != null) {
            accurevBinDir = System.getenv("ACCUREV_BIN");
            if (new File(accurevBinDir).exists() && new File(accurevBinDir).isDirectory()) {
               logger.info("The ACCUREV_BIN environment variable was set to: " + accurevBinDir);
               return accurevBinDir;
            } else {
               try {
                  throw new FileNotFoundException(
                        "The ACCUREV_BIN environment variable was set but the path it was set to does not exist OR it is not a directory. Please correct the path or unset the variable. ACCUREV_BIN was set to: "
                              + accurevBinDir);
               } catch (FileNotFoundException e) {
                  e.printStackTrace();
               }
            }
         }
        
         if (System.getProperty("accurev.bin") != null) {
            accurevBinDir = System.getProperty("accurev.bin");
            if (new File(accurevBinDir).exists() && new File(accurevBinDir).isDirectory()) {
               logger.info("The accurev.bin system property was set to: " + accurevBinDir);
               return accurevBinDir;
            } else {
               try {
                  throw new FileNotFoundException(
                        "The accurev.bin system property was set but the path it was set to does not exist OR it is not a directory. Please correct the path or unset the property. 'accurev.bin' was set to: "
                              + accurevBinDir);
               } catch (FileNotFoundException e) {
                  e.printStackTrace();
               }
            }
         }    
        
         return accurevBinDir;
      }
     
      // This method will populate the depots in the select box depending upon the
      // server selected.
      public ListBoxModel doFillDepotItems(@QueryParameter String serverName, @QueryParameter String depot) {
       
         final AccurevServer server = getServerAndPath(serverName);
         if (server == null) {       
            return new ListBoxModel();
         }

         ListBoxModel d = null;
         Option temp = null;
         List<String> depots = new ArrayList<String>();
        
         // Execute the login command first & upon success of that run show depots
         // command. If any of the command's exitvalue is 1 proper error message is
         // logged
         try {
         if (Login.accurevLoginfromGlobalConfig(server, accurevPath, descriptorlogger)) {
           depots = ShowDepots.getDepots(server, accurevPath, descriptorlogger);
          }
       } catch (IOException e) {        
        
       } catch (InterruptedException e) {
        
       }

         d = new ListBoxModel();
         for (String dname : depots) {
            d.add(dname, dname);
         }
         // Below while loop is for to retain the selected item when you open the
         // Job to reconfigure
         Iterator<Option> depotsIter = d.iterator();
         while (depotsIter.hasNext()) {
            temp = depotsIter.next();
            if (depot.equals(temp.name)) {
               temp.selected = true;
            }
         }
         return d;
      }

      // Populating the streams
      public ComboBoxModel doFillStreamItems(@QueryParameter String serverName, @QueryParameter String depot) {
         ComboBoxModel cbm = new ComboBoxModel();
         final AccurevServer server = getServerAndPath(serverName);
         if (server == null) {
            //descriptorlogger.warning("Failed to find server.");
            return new ComboBoxModel();
         }    
         // Execute the login command first & upon success of that run show streams
         // command. If any of the command's exitvalue is 1 proper error message is
         // logged     
         try {        
             if (Login.accurevLoginfromGlobalConfig(server, accurevPath, descriptorlogger)) {          
             cbm = ShowStreams.getStreamsForGlobalConfig(server, depot, accurevPath, cbm, descriptorlogger);           
            }

         } catch (IOException e) {     
        
       } catch (InterruptedException e) {     
        
       }
         return cbm;
      }

      public static void lock() {
         ACCUREV_LOCK.lock();
      }

      public static void unlock() {
         ACCUREV_LOCK.unlock();
      }
   }
   
    // --------------------------- Inner Class ---------------------------------------------------
    @SuppressWarnings("serial")
  public static final class AccurevServer implements Serializable {

             
      private String name;
        private String host;
        private int port;
        private String username;
        private String password;
        private transient List<String> winCmdLocations;
        private transient List<String> nixCmdLocations;
        private String validTransactionTypes;
        private boolean syncOperations;
        private boolean minimiseLogins;
        private boolean useNonexpiringLogin;
        private boolean useRestrictedShowStreams;

        /**
         * The default search paths for Windows clients.
         */
        private static final List<String> DEFAULT_WIN_CMD_LOCATIONS = Arrays.asList(//
                "C:\\opt\\accurev\\bin\\accurev.exe", //
                "C:\\Program Files\\AccuRev\\bin\\accurev.exe", //
                "C:\\Program Files (x86)\\AccuRev\\bin\\accurev.exe");

        /**
         * The default search paths for *nix clients
         */
        private static final List<String> DEFAULT_NIX_CMD_LOCATIONS = Arrays.asList(//
                "/usr/local/bin/accurev", //
                "/usr/bin/accurev", //
                "/bin/accurev", //
                "/local/bin/accurev",
                "/opt/accurev/bin/accurev");

        public static final String VTT_DELIM = ",";
        // keep all transaction types in a set for validation
        private static final String VTT_LIST = "chstream,defcomp,mkstream,promote";
        private static final Set<String> VALID_TRANSACTION_TYPES = new HashSet<String>(Arrays.asList(VTT_LIST
                .split(VTT_DELIM)));
       // public static final String DEFAULT_VALID_TRANSACTION_TYPES = "add,chstream,co,defcomp,defunct,keep,mkstream,move,promote,purge,dispatch";
        public static final String DEFAULT_VALID_STREAM_TRANSACTION_TYPES = "chstream,defcomp,mkstream,promote";
        public static final String DEFAULT_VALID_WORKSPACE_TRANSACTION_TYPES = "add,chstream,co,defcomp,defunct,keep,mkstream,move,promote,purge,dispatch";
       

        /**
         * Constructs a new AccurevServer.
         */
        public AccurevServer() {
            this.winCmdLocations = new ArrayList<String>(DEFAULT_WIN_CMD_LOCATIONS);
            this.nixCmdLocations = new ArrayList<String>(DEFAULT_NIX_CMD_LOCATIONS);
        }

        @DataBoundConstructor
        public AccurevServer(//
                String name, //
                String host, //
                int port, //
                String username, //
                String password, //
                String validTransactionTypes, //
                boolean syncOperations, //
                boolean minimiseLogins, //
                boolean useNonexpiringLogin, //
                boolean useRestrictedShowStreams) {
            this();
            this.name = name;
            this.host = host;
            this.port = port;
            this.username = username;
            this.password = Password.obfuscate(password);
            this.validTransactionTypes = validTransactionTypes;
            this.syncOperations = syncOperations;
            this.minimiseLogins = minimiseLogins;
            this.useNonexpiringLogin = useNonexpiringLogin;
            this.useRestrictedShowStreams = useRestrictedShowStreams;
        }

        /**
         * When f:repeatable tags are nestable, we can change the advances page of the server config to
         * allow specifying these locations... until then this hack!
         * @return This.
         * @throws ObjectStreamException
         */
        private Object readResolve() throws ObjectStreamException {
            if (winCmdLocations == null) {
                winCmdLocations = new ArrayList<String>(DEFAULT_WIN_CMD_LOCATIONS);
            }
            if (nixCmdLocations == null) {
                nixCmdLocations = new ArrayList<String>(DEFAULT_NIX_CMD_LOCATIONS);
            }
            return this;
        }

        /**
         * Getter for property 'name'.
         *
         * @return Value for property 'name'.
         */
        public String getName() {
            return name;
        }

        /**
         * Getter for property 'host'.
         *
         * @return Value for property 'host'.
         */
        public String getHost() {
            return host;
        }

        /**
         * Getter for property 'port'.
         *
         * @return Value for property 'port'.
         */
        public int getPort() {
            return port;
        }

        /**
         * Getter for property 'username'.
         *
         * @return Value for property 'username'.
         */
        public String getUsername() {
            return username;
        }

        /**
         * Getter for property 'password'.
         *
         * @return Value for property 'password'.
         */
        public String getPassword() {
            return Password.deobfuscate(password);
        }

        /**
         * Getter for property 'nixCmdLocations'.
         *
         * @return Value for property 'nixCmdLocations'.
         */
        public String[] getNixCmdLocations() {
            return nixCmdLocations.toArray(new String[nixCmdLocations.size()]);
        }

        /**
         * Getter for property 'winCmdLocations'.
         *
         * @return Value for property 'winCmdLocations'.
         */
        public String[] getWinCmdLocations() {
            return winCmdLocations.toArray(new String[winCmdLocations.size()]);
        }

        /**
         *
         * @return returns the currently set transaction types that are seen as
         *         valid for triggering builds and whos authors get notified
         *         when a build fails
         */
        public String getValidTransactionTypes() {
            return validTransactionTypes;
        }

        /**
         *
         * @param validTransactionTypes
         *            the currently set transaction types that are seen as valid
         *            for triggering builds and whos authors get notified when a
         *            build fails
         */
        public void setValidTransactionTypes(String validTransactionTypes) {
            this.validTransactionTypes = validTransactionTypes;
        }

        public boolean isSyncOperations() {
            return syncOperations;
        }

        public void setSyncOperations(boolean syncOperations) {
            this.syncOperations = syncOperations;
        }

        public boolean isMinimiseLogins() {
            return minimiseLogins;
        }

        public void setMinimiseLogins(boolean minimiseLogins) {
            this.minimiseLogins = minimiseLogins;
        }

        public boolean isUseNonexpiringLogin() {
            return useNonexpiringLogin;
        }

        public void setUseNonexpiringLogin(boolean useNonexpiringLogin) {
            this.useNonexpiringLogin = useNonexpiringLogin;
        }

        public boolean isUseRestrictedShowStreams() {
            return useRestrictedShowStreams;
        }

        public void setUseRestrictedShowStreams(boolean useRestrictedShowStreams) {
            this.useRestrictedShowStreams = useRestrictedShowStreams;
        }

        public FormValidation doValidTransactionTypesCheck(@QueryParameter String value)//
                throws IOException, ServletException {
            final String[] formValidTypes = value.split(VTT_DELIM);
            for (final String formValidType : formValidTypes) {
                if (!VALID_TRANSACTION_TYPES.contains(formValidType)) {
                    return FormValidation.error("Invalid transaction type [" + formValidType + "]. Valid types are: "
                            + VTT_LIST);
                }
            }
            return FormValidation.ok();
        }

    }

     
    // -------------------------- INNER CLASSES --------------------------
    /**
     * Class responsible for parsing change-logs recorded by the builds.
     * If this is renamed or moved it'll break data-compatibility with old builds.
     */
    private static final class AccurevChangeLogParser extends ParseChangeLog {
    }
}
TOP

Related Classes of hudson.plugins.accurev.AccurevSCM$AccurevSCMDescriptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.