Home > Articles

The First Iteration

This chapter is from the book

CVS Access and Logins

Ant supports several version-control systems, either natively or through the use of optional tasks. The version-control system called Concurrent Versions System (CVS) is used by Ant's developers to manage the Ant source code and is the version-control system that is the most integrated with Ant. CVS is widely used in both open-source and commercial software development, and it is available on most platforms. Although we focus on CVS here, the concepts demonstrated in this book can be applied to almost any revision-control system.

CVS Login

CVS has a repository that contains the revision history of each of the files. The location of this repository typically is referred to as CVSROOT. Usually, an environment variable named CVSROOT is set, and CVS commands implicitly make use of it to determine which repository to act on. The value of CVSROOT also can be passed as a parameter to CVS commands.

Most CVS tasks in Ant behave in the same manner as their CVS command-line counterparts. If the CVSROOT environment variable is set, the Ant tasks pick it up automatically. CVSROOT also can be set as an attribute on the CVS tasks. The one exception is the <cvslogin> task, which requires CVSROOT to be set as an attribute because it ignores the environment variable.

From a user's viewpoint, logging into CVS is not a step that takes place every time the developer accesses CVS. When a user logs in to CVS, a local password file, typically named .cvspass, is created. This file is used to authenticate the user on subsequent calls to CVS. So, the CVS login command doesn't actually log in the user, as most systems do, but it creates the password file in preparation for later CVS commands. Therefore, entering an invalid password when logging in does not do anything. The result is seen only when the first CVS command is run. Listing 3.18 shows the cvsLogin target.

Listing 3.18 cvsLogin Target

<!-- cvsLogin target -->
  <target name="cvsLogin">
    <input message="Please enter CVS password:"
        addproperty="cvsPassword" />
  
    <condition property="noCVSPassword">
      <equals arg1="" arg2="${cvsPassword}" />
    </condition>
  
    <fail if="noCVSPassword">You did not enter your CVS password.</fail>
  
    <cvspass cvsroot=":local:/usr/local/cvsArchive"
         password="${cvsPassword}" />
  </target>

This target begins by incorporating the concepts from the backup tasks, where the <input> task was used to obtain the cvsPassword from the user. If no password is entered, the task fails with an appropriate error message.

Next we call the cvspass task to set the cvsroot and password attributes. The other optional attribute is passfile, which defaults to .cvspass if not overridden.

In reality, the cvsLogin target rarely is called because, in most installations of CVS, the login process is an operation that takes place once per user per machine. However, if users move from machine to machine, or if new users frequently are added, the frequency of logins could increase tremendously. Even if logging into CVS needs to happen more frequently, this step should occur naturally during the build process.

A Custom Task to Check for Required Input Parameters

Story

After a number of changes to the buildfile have been made to check that mandatory parameters are set, it becomes apparent to the developers that there is a lot of replication throughout the buildfile to check for required input parameters. Besides being sloppy, this replication creates a maintenance problem. The development team agrees that a pair of developers should write a custom task to clean this up and consolidate this functionality into one place.

The buildfile has grown to include a lot of constructs to verify that mandatory parameters are set. Recall from Chapter 2, "Creating Initial Spikes," that properties are immutable and can't be used like ordinary variables in a scripting language. Because of this, we wind up setting a number of temporary parameters to check for required input, and the buildfile becomes unwieldy. Also, whenever we want to require parameters in a buildfile, we wind up rewriting the same XML. The team decides to create a custom task that handles required input. This consolidates the checking of required input into a single task and also enables them to write and debug this code once. Listing 3.19 shows the custom task RequiredInput, which extends the Ant Input task. The Input task is not an abstract base class listed in the Ant documentation as a task specifically intended for extension, but there's also no reason why other tasks in Ant can't be extended.

A Task class in general has private attributes, public setter methods for each attribute, and a public execute method. When the task executes, the Ant framework calls the setter method for each attribute that is set in the XML tag. Then the public execute method is called to do the actual work. Appendix B, "Extending Ant," explains the general procedure for writing a custom task as well as methods for setting nested elements.

One of the problems faced in writing this task is that it's necessary to know the value of the attributes of the Input class, but they're private and the derived class doesn't have access to them. The way around this problem is to override the setter classes. Each setter method calls the base class's setter method (such as super.methodname()) and then stores the value of the attribute in the class. This grants access to a copy of the attribute values in the derived class.

When the RequiredInput task executes, it checks the boolean values to determine whether all the required attributes have been set. If they haven't, the value of the unpopulated attribute is false. The execute method then throws an Ant BuildException, which fails the build and prints an error message indicating which required parameter hasn't been set. This task is shown in Listing 3.19, which extends the Input task and simplifies the process of checking for mandatory parameters.

Listing 3.19 A Custom Task for Checking for Required Input

package com.networksByteDesign.util;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.Input;

public class RequiredInput extends Input
{
  /**
   * Defines the name of a property to be created from input. Behavior is
   * according to property task, which means that existing properties
   * cannot be overridden.
   *
   * @param addproperty Name for the property to be created from input
   */
  public void setAddproperty (String addproperty)
  {
    super.setAddproperty(addproperty);
    propertyName   = addproperty;
    havePropertyName = true;
  }
  
  /**
   * Returns the property that gets set during the build run.
   * @return The property being set.
   */
  public String getProperty()
  {
    return propertyName;
  }

  /**
   * Sets the Error Message which gets displayed to the user during
   * the build run.
   * @param errorMessage The error message to be displayed.
   */
  public void setErrormessage (String errorMessage)
  {
    this.errorMessage = errorMessage;
    haveErrorMessage = true;
  }

  /**
   * Returns the Error Message which gets displayed to the user during
   * the build run.
   * @return The error message to be displayed.
   */
  public String getErrormessage()
  {
    return errorMessage;
  }

  /**
   * Actual test method executed by ant.
   * @exception BuildException
   */
  public void execute () throws BuildException
  {
    if (!havePropertyName)
    {
      throw new BuildException("Missing attribute propertyName",
                   location);
    }

    if (!haveErrorMessage)
    {
      throw new BuildException("Missing attribute errorMessage",
                   location);
    }

    super.execute();

    if(getProject().getProperty(propertyName).trim().length() == 0)
    {
      throw new BuildException(errorMessage, location);
    }
  }

  private String propertyName   = "";
  private String errorMessage   = "";
  private boolean haveErrorMessage = false;
  private boolean havePropertyName = false;
}

Appendix B describes the integration of a custom task in detail. One method of hooking a custom task into an Ant buildfile is to declare a mapping between the classname and the taskname with a <taskdef> tag, as shown here:

  <taskdef name="requiredInput"
      classname="com.networksByteDesign.util.RequiredInput"/>

Afterward, all that's required is to put the classfile in your system CLASSPATH, and then run the buildfile. There is an alternate, more "permanent" way to integrate a task, which is described in Appendix B.

Listing 3.20 shows the custom RequiredInput task hooked into our cvsLogin target.

Listing 3.20 RequiredInput Task Hooked into Build File

<?xml version="1.0" ?>
<project name="eMarket" default="compile" basedir=".">

  <taskdef name="requiredInput"
      classname="com.networksByteDesign.util.RequiredInput"/>

  <property name="dirs.source" value="/usr/projects/eMarket/src" />
  <property name="dirs.backup" value="${user.home}/backup"    />
  <property name="dirs.temp"  value="/tmp"           />

  <!-- cvsLogin target -->
  <target name="cvsLogin">
    <requiredInput message="Please enter CVS password:"
            addproperty="cvsPassword"
            errorMessage=" You didn't enter your CVS password."/>

    <cvspass cvsroot=":local:/usr/local/cvsArchive"
         password="${cvsPassword}" />
  </target>

  <!-- compile target -->
  <target name="compile" description="Compile all of the source code.">
    <javac srcdir="${dirs.source}" />
  </target>
</project>

CVS Initialization

Now that we have refactored our Ant buildfile to deal with required input tasks, let's make the CVS login process transparent to the user. Because the CVS login is typically a rarely used target, we'd like to make sure it handles the login when it is necessary but doesn't get in the way of normal usage.

To accomplish this, we'll take a look at the <available> task. The purpose of the <available> task is to allow a specified property to be set if a particular resource, such as a file, a class, a directory, or a JVM system resource, exists. In this case, you can make the CVS login process transparent by looking for the .cvspass file. If it exists, you proceed normally. Otherwise, you prompt the user for the CVS password and create the .cvspass file. Listing 3.21 shows the cvsInit target.

Listing 3.21 cvsInit Target

<!-- cvsInit target -->
  <target name="cvsInit">
    <available file="${user.home}/.cvspass"
          property="cvsAlreadyLoggedIn" />

    <antcall target="cvsLogin" />
  </target>

Next we modify the cvsLogin task to check for the cvsAlreadyLoggedIn property. Instead of using the if attribute as before, we use the unless attribute, which has the opposite effect. These changes are shown in Listing 3.22.

Listing 3.22 cvsLogin Target (Revised)

<!-- cvsLogin target -->
  <target name="cvsLogin" unless="cvsAlreadyLoggedIn">
    <requiredInput message="Please enter CVS password:"
            addproperty="cvsPassword"
            errorMessage=" You didn't enter your CVS password."/>

    <cvspass cvsroot=":local:/usr/local/cvsArchive"
         password="${cvsPassword}" />
  </target>

Now we can make all CVS targets depend on cvsInit, which ensures that the user is logged in if he has not previously done so. If a user already has logged in, the cvsLogin task will be bypassed.

The CVS Task

Now that you have handled the initial access into CVS, you can turn your attention to the <cvs> task itself. In its simplest form, the <cvs> task is simply a pass-through mechanism to the cvs command itself. All the CVS commands and options can run through the <cvs> task as well. Listing 3.23 shows the output from calling cvs --help-command. For additional information about CVS and a list of CVS commands and options, see http://www.cvshome.org/docs/manual/.

Installation of CVS

To install CVS, follow these steps:

  1. Download CVS from http://www.cvshome.org

  2. CVS is available for a number of different platforms. Be sure to check the installation instructions for your specific platform.

  3. The CVS manual can be found at: http://www.cvshome.org/docs/manual

Listing 3.23 Output from the cvs --help-command

% cvs --help-command
CVS commands are:
    add     Add a new file/directory to the repository
    admin    Administration front end for rcs
    annotate   Show last revision where each line was modified
    checkout   Checkout sources for editing
    commit    Check files into the repository
    diff     Show differences between revisions
    edit     Get ready to edit a watched file
    editors   See who is editing a watched file
    export    Export sources from CVS, similar to checkout
    history   Show repository access history
    import    Import sources into CVS, using vendor branches
    init     Create a CVS repository if it doesn't exist
    log     Print out history information for files
    login    Prompt for password for authenticating server.
    logout    Removes entry in .cvspass for remote repository.
    rdiff    Create 'patch' format diffs between releases
    release   Indicate that a Module is no longer in use
    remove    Remove an entry from the repository
    rtag     Add a symbolic tag to a module
    status    Display status information on checked out files
    tag     Add a symbolic tag to checked out version of files
    unedit    Undo an edit command
    update    Bring work tree in sync with repository
    watch    Set watches
    watchers   See who is watching a file
(Specify the --help option for a list of other help options)

CVS Checkouts

The first command we'll look at is the CVS checkout command. This command is used to retrieve a set of files from CVS into a new workspace. As with all CVS commands, you can configure a multitude of options and parameters to meet your specific needs. This book is not intended to be a tutorial on CVS, so we simply pick from some of the more common sets of options.

One of the advantages of using Ant for your build and deployment process is that you can easily check Ant buildfiles into version control, and you can track revisions of your buildfile as well. On many of our projects, the Ant buildfile had more revisions than the source code as we enhanced our build and deployment process throughout the lifecycle of the project. In fact, the buildfiles used as examples throughout this book were maintained in a CVS repository. The issue with keeping the Ant buildfile under version control is that it creates a circular dependency. To run a target on an Ant buildfile, you first must check the workspace out of version control. When the workspace has been checked out, the need for an Ant target to check out a new workspace is negated.

However, if the Ant file is contained in a separate workspace from the rest of the source code, a checkout target for pulling a particular release of the source code makes perfect sense. You will apply this technique later in the book when application deployment is discussed. For example, we have an application that has its installation CD built with Ant. The Ant buildfile checks out a copy of Tomcat and JBoss from CVS and includes it as part of the installation.

If you are familiar with other revision-control systems but not with CVS, there is an important distinction in the concept of checkout. In revision-control systems such as the Revision Control System (RCS), the Source Code Control System (SCCS), and Clearcase, checking out a file usually means retrieving a copy of it with the intent of modifying it. This means that the file is locked in the repository, preventing others from modifying the file until the person who locked the file releases the lock. By contrast, in CVS, checking out means pulling a copy of the source code, but not necessarily with the intent to modify any of it. Also, the checkout command can be used to pull code based on some criteria such as a tag, all the latest versions on the main branch, and so on. The important distinction here between CVS and other revision-control systems is that the term checkout doesn't mean that the repository is locked for modification, as it implies in most other revision-control systems.

The concepts presented so far will assist you in building robust, reusable targets that can be used interactively by a developer, but also as part of an unattended build and deployment process.

Listing 3.24 shows the cvsCheckout Target. This target depends on cvsInit, which ensures that the user has previously logged in to CVS, either through the Ant rules or through CVS directly.

Listing 3.24 cvsCheckout Target

<!-- cvsCheckout target -->
  <target name="cvsCheckout" depends="cvsInit">

    <requiredInput message="Please enter CVS module:"
            addproperty="cvsModule"
            errorMessage="You didn't enter a CVS module." />

    <requiredInput message="Please enter working directory:"
            addproperty="dirs.working"
            errorMessage="You didn't enter a working directory"/>

    <mkdir dir="${dirs.working}" />

    <cvs package="${cvsModule}" dest="${dirs.working}" />
  </target>

As with the backup targets, the user is prompted to provide the name of the module to check out from CVS, as well as the directory in which to place the checked-out code. Modules are a convenient way in CVS to group files and directories that are logically related. If the module or working directory is not properly entered, the target fails.

<mkdir dir="${dirs.working}" />

We then create the working directory in case it does not exist. As you learned earlier, if it does exist, the default behavior is to do nothing.

<cvs package="${cvsModule}" dest="${dirs.working}" />

Next, call the cvs task, passing the module that you want to check out in the package attribute and the working directory in the dest attribute. The default command for the <cvs> target, if no command is supplied, is to check out a new workspace; for the purposes of this discussion, you do not need to be concerned with the cvs options passed to the checkout command.

The end result of the cvsCheckout target is a checked-out CVS module in the directory of your choice. As with the backup process, you can break the interactive part of cvsCheckout into a separate target so that you can produce an unattended version as well.

CVS Updates and Commits

Now that we have checked out a version of the source code, we need a way to pull the latest changes that other developers on the team are checking in. In CVS, this is done through the use of the update command. As with the checkout command, all CVS commands and options can be used in the command attribute of the cvs task. We must add the attribute to the new target because we relied on the default value for the checkout target.

The following code shows the cvsUpdate target. The module name is not required because CVS looks in the ${dirs.source} to find the information it needs regarding the CVSROOT and the module where the source code is mastered. The update command displays a list of files that have been changed, updated, or added since the last update. For example, a ? in front of the filename indicates that this is a new file, an M indicates a modified file, and a C indicates that changes you have made conflict with other changes made to the same file and that CVS cannot resolve the conflicts automatically. Information on the meaning of each of the flags, as well as how to work with issues such as conflicts, can be found on the CVS Web site.

  <!-- cvsUpdate target -->
  <target name="cvsUpdate" depends="cvsInit">
    <cvs command="update" dest="${dirs.source}" />
  </target>

In CVS, putting your changes into the repository is done with the commit command. This is analogous to the concept of checkin with other revision-control systems. Committing changes is straightforward. CVS prompts you to enter a comment describing the changes, which is always a good practice to follow. CVS attempts to use your default editor for the entering of the comments (the value of the EDITOR environment variable in UNIX, for example). The following code shows the Ant task for committing changes to CVS.

  <!-- cvsCommit target -->
  <target name="cvsCommit" depends="cvsInit">
    <cvs command="commit" dest="${dirs.source}" />
  </target>

If you would rather use the <input> task for adding comments as you have been doing, just add the appropriate tasks, as shown in Listing 3.25.

Listing 3.25 cvsCommit Target (Revised)

<!-- cvsCommit target -->
  <target name="cvsCommit" depends="cvsInit">
    <requiredInput message="Please enter your CVS comment:"
            addproperty="cvsComment"
            errorMessage=" You did not enter a CVS comment."/>
    <cvs command="commit -m ${cvsComment}" dest="${dirs.source}" />
  </target>

In this case, we are enforcing a team policy in the build process rather than relying only on verbal and written policies and procedures. If the developer does not enter a cvs comment, the commit process will abort. If a comment is added, commit is called, with the –m flag passing the comment as a parameter.

The cvsUpdate and cvsCommit targets work fine until two developers change the same file. Even if the changes are not in conflict, if one developer does not call cvsUpdate before cvsCommit, cvsCommit will fail. Although the error message will indicate what went wrong, it is better to change cvsCommit to be dependent on cvsUpdate.

<target name="cvsCommit" depends="cvsUpdate" description="">

If the changes are in conflict, the update will indicate this and the commit will fail with an appropriate error message. This rule will check in all your changes with the same comment. Depending on your team standards, this might not be appropriate. However, if you make small, logical changes to the source code, this approach can be a best practice. By concentrating on the reason for the change rather than the specific code changes, which can be easily gleaned from a comparison of the two versions, a reviewer of the comments can better understand the motivation for certain changes.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020