Pages

Showing posts with label configuration management. Show all posts
Showing posts with label configuration management. Show all posts

Tuesday, November 29, 2011

Putting It All Together: Software Tools and Issue-Driven Project Management

In this semester, I have been completing various skill-learning exercises called "katas" to quickly learn the basics of some software tools designed to make Java development easier, faster, and more error-free. Likewise, through my own struggles, successes, and failures, I have learned that these tools are only as good as the people who use them. Ultimately, any organization is only as good as its people, and no class on software engineering would be complete without at least one project that needed to be organized through a project team.

Over the last two weeks, I have been working with Christopher Foo and Eldon Visitacion to design a console application using the WattDepot API. Throughout the duration of the project, in addition to the Subversion, Ant, Eclipse, and JUnit tools discussed in past entries, we began using a class Jenkins server as a continuous integration tool. Configuring Jenkins to automatically build and test the project after each repository change helped us to pinpoint which Subversion commits introduced faulty code into the system and which developers were responsible, making us more careful about verifying our code in the first place. We used Google Project Hosting to manage our repository and a small wiki.

Our work and school schedules prevented us from meeting in person very often, so we designated issues and set intermediate deadlines mostly through email. Each group member was responsible for posting and closing their own issues and notifying the member assigned to a certain part of the project of any bugs they found while examining or testing each others' code, and all group members were responsible for testing the final .jar executable.

The Hale Aloha CLI

The console application's purpose was to retrieve data from the WattDepot server for the University of Hawaii at Manoa's Hale Aloha freshman residential complex, consisting of four towers where energy consumption and power data were being logged for the Kukui Cup. It needed to display the current levels of power being used, the amount of energy consumed on a given day, the energy consumed since a given day, and the rankings in energy consumption of the four towers from a given start date to a given end date.

The Command Line Interface

Command-line interface.
The command-line interface was designed by Christopher Foo. The Control class passes commands to the CommandProcessor class, which stores a TreeMap of classes which implement the Command interface. The Command interface specifies two methods. The first is an execute method, which constructs the object that carries out the command, initializes its fields, and prints the data retrieved from the server as a string. The second is a getHelpString method, which prints the correct syntax to use the command if the command "help" is issued. Typing "quit" exits the program. The available commands can be extended by adding the new Command-implementing class to the TreeMap.

Our team's implementation does not use Java's Reflect class.

Current Power

>current-power [tower | lounge]
current-power
The Current Power command was designed by Eldon Visitacion. It retrieves the newest available power consumption data recorded by a tower or lounge source's sensor on the current day. Data is in kilowatts.

Daily Energy

>daily-energy [tower | lounge] [date]
energy-since
The Daily Energy command was designed by Eldon Visitacion. It retrieves the newest available total energy consumption recorded by a tower or lounge source's sensor on a given date, in yyyy-mm-dd format. Data is in kilowatt-hours.

Energy Since

>energy-since [tower | lounge] [date]
energy-since
The Energy Since command was designed by me. It retrieves the total energy consumed by a tower or lounge source on a given date, in yyyy-mm-dd format. Data is in kilowatt-hours.

Rank Towers

>rank-towers [start] [end]
rank-towers
The Rank Towers command was designed by me. It retrieves the total energy consumed by each of the four Hale Aloha towers from a given start date to a given end date, both in yyyy-mm-dd format. Data is in kilowatt-hours.

JUnit Tests

JUnit tests were incorporated into our system fairly early on. The first draft tests for EnergySince were running by November 12 (continuous integration build 15). The first tests for Control, CommandProcessor, and Quit were posted by November 15 (build 25). CurrentPower and DailyEnergy tests first appear on November 16 (build 27), a test for Help was posted on November 17 (build 38), and the first version of TestRankTowers was also committed on November 17 (build 39). JUnit tests had mostly reached their final states by Build 54 on November 24, and the system stayed at a consistent level of at least eighty percent instruction coverage afterwards.

Since parts of our command implementations were dependent on the time reported by the server, there were some pieces of my code - mostly those that converted time information to strings or to some other format based on the current date - which could not be tested. Despite this, I was able to test most of the methods used in my classes, especially the utility methods responsible for creating the XMLGregorianTimestamp objects used to retrieve energy consumption data. As I had chosen to make all of my setter and utility methods private, I had to learn to use Java's Reflect class to invoke them in tests. Oracle's tutorial and Lasse Koskela's exception testing examples were very useful here.

Another odd problem I had was using Reflection to invoke a method with a parameter that was a non-primitive-type array. If the parameter is just declared as foo[].class, Java interprets it as a varargs parameter instead of an array. In this case, I was trying to test the setter method for an array of XMLGregorianCalendar timestamps.

The correct way to do this, described at Your Mitra here, is to declare the array as a new class which contains the array:

Method testSetEnd = testClass.getDeclaredMethod("setEndTimestamps", new
Class[]{XMLGregorianCalendar[].class});

Then, in the method invocation, the array being passed as a parameter needs to be cast to a new class as well:

XMLGregorianCalendar sampleTimestamp =
Tstamp.makeTimestamp("2011-11-11T11:11:11.111-11:00");
XMLGregorianCalendar[] holdTimestamps = new XMLGregorianCalendar[]{sampleTimestamp,
sampleTimestamp};
// Cast the array to an object, otherwise it will be treated as varargs.
testSetEnd.invoke(testObject, new Object[]{holdTimestamps});

The large number of date-processing utility methods in my classes made it difficult to test all possible branches of the code, especially those which executed based on times retrieved from the WattDepot server. We also did not implement tests for the main method in the Control package, which we tested manually. JUnit’s policy of factoring in what percentage of test case code was executed greatly decreased the branch coverage score: the test which branched based on the time returned from the server executed only a few of their branches in the average unit testing. In the end, we achieved roughly 85% instruction coverage and 80% branch coverage.

Our software system is thoroughly documented and contains input-checking code that ensures that the right number of command-line arguments are given, dates do not overlap with each other or exceed the current date, and that source names match with an existing source's name, and most or all of these input checks are intentionally triggered in each class's Junit tests by passing bad input to the each Command's execute method. In addition, all of the Commands which take date parameters are capable of collecting data for the current day up to the most recent data reading from the requested source. Though it has its flaws, I consider it to be fairly high-quality in its error checking, operation, and test case coverage.


Server Issues

Though it had been difficult and at times annoying to get used to continuous integration practices, it was harder to coordinate system changes when the Jenkins server briefly crashed, or when it began automatically failing builds when the WattDepot server crashed and our Junit tests failed automatically after being unable to establish server connections. As the cliche goes, "You don't know what you got till it's gone." For all the annoyance of having a Jenkins build fail over some minor formatting error, it was still preferable to having that error remain in the code because the committer forgot to verify it beforehand.

In the end, Jenkins forced me to adopt better development practices by encouraging me to obsessively run Checkstyle, PMD, FindBugs, and JUnit tests - both in order to commit workable code, and to avoid having my usually careless errors broadcast to my team members. This makes continuous integration an effective tool for pinpointing which committers are responsible for errors and encouraging better practices (though maybe not quite as effective as a foam rocket to the head).


Debugging A Time Problem

The other server-related issue I experienced was in retrieving energy consumption data from the server at midnight. The WattDepotClient.getEnergyConsumed method retrieves data given a source name, two XMLGregorianCalendar objects representing the start and end times for data collection, and an integer representing the sampling interval in minutes. The original versions of my code had the interval set to 15 minutes (or 96 data points per 24-hour period). I noticed that JUnit tests would always fail mysteriously for current-day data requests made within about twenty minutes of midnight, throwing BadXMLExceptions which stated that my time range requested data beyond the available range of data on the server. Since my ending time was always based on the timestamp of each source's most recent sensor data on the server, my start time was hard-coded to midnight of the starting day, and I was in the same time zone as the server, I knew I was not experiencing synchronization issues between local time and the server. Further testing on November 24 revealed that the problem disappeared at about 12:17 AM, which was suspiciously close to my sampling interval. When I reported my problem to the class mailing list, Professor Johnson confirmed that the bug was the result of my sampling interval exceeding the range of available data. The solution was to set the sampling interval to 0 minutes, which allowed it to make data requests even when the starting time and the ending time were exactly the same.


Conclusion

Learning about the proper use of the tools of automated quality assurance and continuous integration has helped me reduce the number of syntactical errors I make in the first place, while making sure that most of my worst development mistakes are eliminated by automated quality assurance before entering the repository. Having team members with whom communication was easy made it faster and less confrontational to diagnose issues and suggest solutions, and enabled our group to proceed at a steady rate. Incorporating unit testing into our builds early on helped us get test coverage up to a reasonably high level by the end of the project, even if it was not possible to test some parts of the code. Ultimately, the combination of good time management and industry-standard quality-assurance tools helped us work around server crashes, data-request bugs, and test coverage problems and see the project to its completion. A good team can compensate for occasionally dysfunctional technology, but even good technology usually cannot save a project from a truly dysfunctional team.

Thursday, October 20, 2011

A Week of Configuration Management

Configuration Management

Configuration management software attempts to identify and track the elements of a system's configuration so that errors can be found and fixed. Version control is a special sub-case of this which focuses on maintaining multiple branches (development versions used for experimentation or developing new features) and the central trunk (the main version which all branches must eventually be merged with). Defect tracking systems track error reports from identification to the point at which the errors are fixed, and are part of configuration management because fixing the errors usually requires configuration changes.

To use a configuration management system, developers must first check out (download) the project from the SVN repository (the directory on a remote server where the project files are stored). Once this is done, they can edit their local copy of the project files, then commit (upload) the changed files or directories. If a developer adds new files to the project, they must be manually added to the files under version control before being committed to the repository. If others make changes, a developer must update (download new versions) the local copies of the changed files. William Wake explains it in more detail in this graphic.

As we were instructed in class, best practices for configuration management systems include verifying that the system runs and builds correctly before committing any changes. Otherwise, you run the risk of having to hurriedly fix previously undetected mistakes one after the other, as reflected in this log of commit messages:
Rev 53571: "This is a basic implementation that works."
Rev 53572: "By works, I meant 'doesnt work'. Works now.."
Rev 53573: "Last time I said it works? I was kidding. Try this."
Rev 53574: "Just stop reading these for a while, ok.. "
Rev 53575: "Give me a break, it's 2am. But it works now."
Rev 53576: "Make that it works in 90% of the cases. 3:30."
Rev 53577: "Ok, 5am, it works. For real.
Back when I said basic implementation? Scratch that."
(Source: This Stack Overflow thread)

Subversion

Subversion, the successor to the earlier CVS, is an open-source configuration management system that runs on the Berkeley Database, manages tags and branches similarly to folders, and handles all file types in the same way. It is currently an industry standard due to its centralized nature, which helps with access control issues and allows a single project leader to coordinate development; however, many in the open-source community prefer Git's flexibility and multiple workflows over Subversion' centralization.

Many GUI and command-line clients exist to access SVN repositories; I used TortoiseSVN, a Windows client. The GUI was fairly easy to use for my first assignment of revising and committing a preexisting project. For the second project, in which I had to upload my project to Google Code (about which more is written in the next section), I had a little more trouble with the command line interface, and what was ultimately more helpful than the project documentation in figuring out how to add files and do other operations was this general SVN manual, which did a good job of describing SVN's general logic.

Google Project Hosting

To complete my training in configuration management, after uploading my code to Google's remotely hosted repository via TortoiseSVN, I set about configuring the front page and some instructions for users and developers working with my robocode-tjo-meteor robot. The page I set up can be found here, and the source files can be checked out using the instructions here.

Google Project Hosting is fairly easy to add new pages to and configure settings for, using a system of simple menus similar to other Google interfaces I have used in Blogger or Google Sites.

Google Project Hosting allows users to choose Subversion, Git, or Mercurial as their configuration management system, and handles defect tracking by listing open issues on the "Issues" page. The current SVN trunk is viewable in the Source page, and the revision history is also available.

The only real issue I have with Google Project Hosting mostly deals with with element positioning:
  • The tables are made with sets of ||s, not tags:Google only detects a table cell if both the opening || and closing || are on the same line (i.e., you never press enter at any point between them).
    For example, typing:

    ||Contents||
    ||I want this entire sentence to appear
    in one table cell||

    results in:
    If you break this rule, you may end up with several scattered partial table cells. The workaround, typing the text you want to enclose as one long sentence, means that enclosing long sentences or paragraphs in table cells looks ugly in code and is almost unreadable if you use any other HTML tags or wiki markup within the table. I spent hours figuring out how to force Google to recognize the table cells before I realized that the bug was related to newline characters in this way. Google does allow the use of <table> tags, however, so there is a workaround for this.
  • The anchor links:There are several things to be aware of when writing these. The support page helpfully explains that spaces need to be replaced with underscores in anchor links. Essentially, what I discovered was this:

    // In this case I am trying to make an anchor link to header == Foo Bar ==
    [# Foo Bar ] // This creates a link to the main URL of the page itself, and reloads it instead of going to the anchor. In some cases it does not work at all. The preceding and trailing spaces are not needed even though they are in the header.
    [#Foo Bar] // This will not work either. As mentioned, the space needs to be replaced with an underscore.
    [#Foo_Bar] // This will work.

    If you are mostly using this to make a table of contents as I did, there is a faster piece of built-in wiki code that does the job:
    //Set the depth to specify how many layers deep you want the contents to go.
  • You cannot import images directly:Blogger allows you to import pictures from within the editor and host them on Picasa, in addition to embedding image links to pictures you have hosted elsewhere. This is not possible in Google Code. Though I there are definitely reasons to prefer this approach (Picasa's limitations, privacy concerns, or Blogger's arbitrary display sizes of "large", "medium", and "small"), a similar feature might be nice as an additional option.
Despite these minor issues (some of which were aggravated by my misreading of Google's support page), Google Code seemed to provide a decent level of services for being a free service and was an easy way for me to learn the basics of version control in a controlled setting, without the risk of damaging a real enterprise project - and demonstrated the ease with which I could use Subversion systems to store personal projects as well, though perhaps not quite at the level of Joey Hess.