JavaRanch Newsletter Articles in this issue:
The SCJP Tip Line - Strings, LiterallyCorey McGlone
Printable Version
Driving On CruiseControl - Part 1Lasse Koskela
Printable Version
Java Designs - Examining Layouts with SwingTom Tolman
Printable Version
Meet the Author - Budi KurniawanDirk Schreckmann
Printable Version
Creating Multiple Language PDFs using Apache FOPBalaji Loganathan
Printable Version
The Trail Guide - The three rules of using XML and JavaPaul Wheaton
Printable Version
Saloon Question and Answer of the Month - An Informal Survey on the Use of AssertDirk Schreckmann
Printable Version
Book Review of the Month - JUnit Recipes: Practical Methods for Programmer TestingDirk Schreckmann
Printable Version
Book Promotions coming in September and October Dirk Schreckmann Printable Version

The SCJP Tip Line - Strings, Literally
The SCJP Tip Line
Strings, Literally
by Corey McGlone

This month, I'm going to tackle String literals and how they're handled in Java. If you read last month's SCJP Tip Line article, you'll find that this article is a nice follow up all about Strings. If you didn't read it, no big deal - I plan to cover enough information here to get you up to speed. String literals are a little "special" in Java in the way that they are treated. For the most part, the information contained in this post is not on the SCJP exam, but I see it on mock exams all the time and it comes up in the SCJP/OCPJP forum quite often. Besides, it's just darned interesting stuff.

Let's start off by asking a simple question. What is a String Literal? A String literal is a sequence of characters between quotation marks, such as "string" or "literal". You've probably used String literals hundreds of times in your own applications. You might not, however, have realized just how special String literals are in Java.

Strings are Immutable

So what makes String literals so special? Well, first of all, it's very important to remember that String objects are immutable. That means that, once created, a String object cannot be changed (short of using something like reflection to get at private data). Immutable, you say? Unchangable? What about this code?

Source Code
            
public class ImmutableStrings
{
    public static void main(String[] args)
    {
        String start = "Hello";
        String end = start.concat(" World!");
        System.out.println(end);
    }
}

// Output

Hello World!
            

Well look at that, the String changed...or did it? In that code, no String object was ever changed. We start by assigning "Hello" to our variable, start. That causes a new String object to be created on the heap and a reference to that object is stored in start. Next, we invoke the method concat(String) on that object. Well, here's the trick, if we look at the API Spec for String, you'll see this in the description of the concat(String) method:
Concatenates the specified string to the end of this string.
If the length of the argument string is 0, then this String object is returned. Otherwise, a new String object is created, representing a character sequence that is the concatenation of the character sequence represented by this String object and the character sequence represented by the argument string.

Examples:

    "cares".concat("s") returns "caress"
    "to".concat("get").concat("her") returns "together"

Parameters:
    str - the String that is concatenated to the end of this String.
Returns:
    a string that represents the concatenation of this object's characters followed by the string argument's characters.
Notice the part I've highlighted in bold. When you concatenate one String to another, it doesn't actually change the String object, it simply creates a new one that contains the contents of both of the original Strings, one after the other. That's exactly what we did above. The String object referenced by the local variable start never changed. In fact, if you added the statement System.out.println(start); after you invoked the concat method, you would see that start still referenced a String object that contained just "Hello". And just in case you were wondering, the '+' operator does the exact same thing as the concat() method.

Strings really are immutable.

Storage of Strings - The String Literal Pool

If you've done any preparation for the SCJP exam (and quite possibly even if you haven't), you've probably heard of the "String Literal Pool." What is the String Literal Pool? Most often, I hear people say that it is a collection of String objects. Although that's close, it's not exactly correct. Really, it's a collection of references to String objects. Strings, even though they are immutable, are still objects like any other in Java. Objects are created on the heap and Strings are no exception. So, Strings that are part of the "String Literal Pool" still live on the heap, but they have references to them from the String Literal Pool.

Yeah, so that doesn't really explain what the pool is, or what it's for, does it? Well, because String objects are immutable, it's safe for multiple references to "share" the same String object. Take a look at this example:

Source Code
            
public class ImmutableStrings
{
    public static void main(String[] args)
    {
        String one = "someString";
        String two = "someString";
        
        System.out.println(one.equals(two));
        System.out.println(one == two);
    }
}

// Output

true
true
            

In such a case, there is really no need to make two instances of an identical String object. If a String object could be changed, as a StringBuffer can be changed, we would be forced to create two separate objects. But, as we know that String objects cannot change, we can safely share a String object among the two String references, one and two. This is done through the String literal pool. Here's how it is accomplished:

When a .java file is compiled into a .class file, any String literals are noted in a special way, just as all constants are. When a class is loaded (note that loading happens prior to initialization), the JVM goes through the code for the class and looks for String literals. When it finds one, it checks to see if an equivalent String is already referenced from the heap. If not, it creates a String instance on the heap and stores a reference to that object in the constant table. Once a reference is made to that String object, any references to that String literal throughout your program are simply replaced with the reference to the object referenced from the String Literal Pool.

So, in the example shown above, there would be only one entry in the String Literal Pool, which would refer to a String object that contained the word "someString". Both of the local variables, one and two, would be assigned a reference to that single String object. You can see that this is true by looking at the output of the above program. While the equals() method checks to see if the String objects contain the same data ("someString"), the == operator, when used on objects, checks for referential equality - that means that it will return true if and only if the two reference variables refer to the exact same object. In such a case, the references are equal. From the above output, you can see that the local variables, one and two, not only refer to Strings that contain the same data, they refer to the same object.

Graphically, our objects and references would look something like this:


Note, however, that this is a special behavior for String Literals. Constructing Strings using the "new" keyword implies a different sort of behavior. Let's look at an example:

Source Code
            
public class ImmutableStrings
{
    public static void main(String[] args)
    {
        String one = "someString";
        String two = new String("someString");
        
        System.out.println(one.equals(two));
        System.out.println(one == two);
    }
}

// Output

true
false
            

In this case, we actually end up with a slightly different behavior because of the keyword "new." In such a case, references to the two String literals are still put into the constant table (the String Literal Pool), but, when you come to the keyword "new," the JVM is obliged to create a new String object at run-time, rather than using the one from the constant table.

In such a case, although the two String references refer to String objects that contain the same data, "someString", they do not refer to the same object. That can be seen from the output of the program. While the equals() method returns true, the == operator, which checks for referential equality, returns false, indicating that the two variables refer to distinct String objects.

Once again, if you'd like to see this graphically, it would look something like this. Note that the String object referenced from the String Literal Pool is created when the class is loaded while the other String object is created at runtime, when the "new String..." line is executed.


If you'd like to get both of these local variables to refer to the same object, you can use the intern() method defined in String. Invoking two.intern() will look for a String object referenced from the String Literal Pool that has the same value as the one you invoked the intern method upon. If one is found, a reference to that String is returned and can be assigned to your local variable. If you did so, you'd have a picture that looks just like the one above, with both local variables, one and two, referring to the same String object, which is also referenced from the String Literal Pool. At that point, the second String object, which was created at run-time, would be eligible for garbage collection.

Garbage Collection

What makes an object eligible for garbage collection? If you're preparing for the SCJP exam (or even if you're not), the answer to that question should roll right off your tongue. An object is eligible for garbage collection when it is no longer referenced from an active part of the application. Anyone see what is special about garbage collection for String literals? Let's look at an example and see if you can see where this is going.

Source Code
            
public class ImmutableStrings
{
    public static void main(String[] args)
    {
        String one = "someString";
        String two = new String("someString");
        
        one = two = null;
    }
}
            

Just before the main method ends, how many objects are available for garbage collection? 0? 1? 2?

The answer is 1. Unlike most objects, String literals always have a reference to them from the String Literal Pool. That means that they always have a reference to them and are, therefore, not eligible for garbage collection. This is the same example as I used above so you can see what our picture looked liked originally there. Once we assign our variables, one and two, to null, we end up with a picture that looks like this:


As you can see, even though neither of our local variables, one or two, refer to our String object, there is still a reference to it from the String Literal Pool. Therefore, the object is not elgible for garbage collection. The object is always reachable through use of the intern() method, as referred to earlier.

Conclusion

Like I said at the outset of this article, virtually none of this information is included on the SCJP exam. However, I constantly see this question coming up in the SCJP forum and on various mock exams. These are a few of the highlights you can keep in mind when it comes to String literals:
  • Equivalent String Literals (even those stored in separate classes in separate packages) will refer to the same String object.
  • In general, String Literals are not eligible for garbage collection. Ever.
  • Strings created at run-time will always be distinct from those created from String Literals.
  • You can reuse String Literals with run-time Strings by utilizing the intern() method.
  • The best way to check for String equality is to use the equals() method.
Be sure to check out these resources, as well:

API Spec for String
JLS, §3.10.5 String Literals

Until next time,
Corey
Discuss this article in The Big Moose Saloon!


Return to Top
Driving On CruiseControl - Part 1

Driving On CruiseControl - Part 1

Lasse Koskela
Accenture Technology Solutions

Continuous Integration is one of the buzzwords most people have probably heard of but surprisingly few are actually following this XP best practice. Keeping this in mind, I'll begin this tutorial by briefly describing what Continuous Integration actually means, why you should consider doing it, and finally, showing step by step how to do it using one of the most used Continuous Integration products, the open source CruiseControl developed by a bunch of ThoughtWorkers.

What is Continuous Integration?

Technically, the term "Continuous Integration" means that everyone on the team integrates their changes back into the source repository frequently, verifying that the changes didn't break anything. These days, most people associate Continuous Integration to be highly automated as well, i.e. having an automatic build system in place to continuously verify that the code in the repository compiles and passes all its tests. The exact definition for "frequently" and "continuously" depends on the team but it serves as a pretty good rule of thumb to say that the frequency should be at least multiple times a day. Personally, I prefer running the integration build at least once an hour.

When I'm referring to the term "Continuous Integration" in this article, I'm talking about running a fully automated build over and over again throughout the day, day after day, against the latest version of the source code and having each developer integrate their changes into the repository as frequently as necessary for keeping your team going fast.

You also may have heard of Daily Builds or Nightly Builds. Continuous Integration is not the same thing as Daily or Nightly Builds. The main differences being three-fold:

  1. Continuous Integration happens throughout the day while Nightly Builds happen once a day.
  2. Nightly Builds aim to produce a stable distributable for use whereas Continuous Integration aims to also produce fast feedback on integration failures (and successes) while pushing out those (mostly) stable releases.
  3. Nightly Builds don't say anything about how often developers should check in, Continuous Integration with its fast feedback goal promotes frequent check ins to facilitate that fast feedback.

"Once an hour, huh?", you might say. "That sounds like a lot of work to integrate up to 7-8 times a day. I mean, we all know how merging one's changes can be a real pain in the behind, right?"

Why should I consider doing it?

There are some very convincing benefits a development team can gain from using Continuous Integration. Some of those benefits can really be understood only by experiencing it for real, but I'll try my best to convey the most important ones right now, before we get into the "how" part and get our hands all dirty.

Remember when I just said that Continuous Integration aims to provide fast feedback? Let me tell you a little story to explain what I'm talking about.

A friend of mine joined a small software development team that had a relatively long history behind it, having produced a number of bi-yearly releases of an in-house project management software. The codebase was relatively big and messy, the developers were unfamiliar with some of the worst spider webs, the build process took ages, and the testing team was often faced with a broken build that couldn't be tested beyond the first screen or so.

Fortunately, the team had gotten a configuration manager, another friend of mine, with enough energy and will to make things better and the team decided to try something new. That something was Continuous Integration.

The configuration manager set up an automated build system that periodically, once every hour, checked out the latest version of all files in the version control and ran the build script, effectively attempting to compile all code and run any automated tests it could find. The build system would send an email to each and every member of the development team whenever the automated build failed.

Obviously the build system was constantly sending out those build failure emails to the whole team as people repeatedly forgot to check in all their changes, failed to compile the source code locally after a small edit, and neglected running the automated tests before checking in.

What was the team's reaction to this new stream of nasty emails complaining about even the smallest error they could make? Yes, you guessed correctly. The team members asked the configuration manager to stop running the automated build so frequently, arguing that their mailboxes were so full of "build failed" emails that they couldn't do any real work from deleting all that spam. After all, they knew the build is broken already from the first copy of that email so there was no need to send another one.

This reaction was no surprise, considering how annoying it is to get the same whining email an hour after hour. So, how did the configuration manager react to this sudden burst of dissatisfaction? He scheduled the automatic builds to run twice as often as they used to.

My friend -- not the configuration manager, the other one -- told me that that was the moment when he realized how wonderful Continuous Integration actually was. The revelation he had at that time was that the purpose of a Continuous Integration system is not just to inform the developers about the build being broken but to keep the build from being broken for extended periods of time. Keeping the build green.

Eventually, after a day or two of constant nagging about the annoyance, it all suddenly stopped. No "build failed" messages twice in an hour, no whining about getting "spammed", no nothing. The build had started to stay green. Of course the build broke every now and then, but it had become an exception instead of the rule and the developers started to value those "build failed" messages as valuable feedback instead of worthless spam. I've heard of developers initially creating mail filters to send anything coming from the build machine to the trash folder only to find themselves addicted to the invaluable feedback a Continuous Integration system can provide.

To me, all this seems to be convincing evidence about there being something about Continuous Integration that is worth exploring further. If you feel the same, please continue reading.

What tools can I employ in doing it?

So, how can we automate this Continuous Integration thing? Are there any tools I can use or do I need to come up with my own home-grown solution?

Building your own piece of software for the job could be a lot of fun. The end result just might be a perfect fit for your project. However, since there are some existing products -- both commercial and open source -- that can do the job and do exhibit a certain degree of configurability, I'm pretty sure you'll be better off starting with an established product. Let's take a brief look at some of them, shall we?

CruiseControl is the clear winner in mind share among CI products for Java developers. It's an open source project run by a bunch of ThoughtWorks employees and remains fairly active. CruiseControl is the product I've chosen to use in this article and it is probably the safest choice for most Java projects to use, considering its significant user base.

AntHill, developed by Urbancode is another reasonably popular alternative for CruiseControl. AntHill comes in two editions, commercial and open source. The commercial version adds a wide array of features but the open source version should be more than enough to get you going. AntHill is not as common as CruiseControl, but it is often quoted as being significantly easier to set up and use than CruiseControl.

CruiseControl.NET is more or less a port of the original Java version for the Microsoft.NET platform. The feature set is pretty close to what the Java version provides.

Draco.NET makes sure that CruiseControl.NET is not the only option for .NET projects either. It's another open source Continuous Integration server for .NET and is definitely a good candidate along with CruiseControl.NET.

DamageControl is the lone wolf among other Continuous Integration servers. It is developed by the CodeHaus open source community (although the developers are currently working for ThoughtWorks). DamageControl earns its lone wolf title by being implemented in Ruby, and provides a fresh alternative for Continuous Integration that should fit both worlds (Java and .NET) equally well.

Tinderbox by the Mozilla project represents a distributed approach to automated, continuous builds on multiple platforms and is worth a look if you're dealing with platform-dependent functionality.

This is not quite all, however. Aslak Hellesoy from ThoughtWorks/Codehaus has started a nice matrix for comparing different CI products, including the ones listed above.

So we have some choices but how do these products differ from each other? Well, generally speaking, not much. They all employ the same overall approach of integrating with a source control repository to get the latest sources, compiling the code and running tests using a build tool like Ant or NAnt, and finally publishing the results via email, a web page, or both. Some tools also provide handy system tray utilities for pushing the green/red information directly to the developers' desktops!

Here's a little diagram for illustrating how these Continuous Integration servers are typically set up in a project environment. Notice how the build server is just another "client" for the repository server.

A typical CI architecture

Setting up continuous integration with CruiseControl

Next, we're going to set up a Continuous Integration system following the overall architecture illustrated above. In order to solve that puzzle, we need to have all the parts available:

  • The repository server
  • The build server
  • The developer machine(s)

...and, of course:

  • Something to build

Since I'm sure not everyone has 4 machines just waiting for their next experiment, we're going to set up all of these "virtual" machines on one box. Here's a screenshot of the directory structure on my Windows box:

The top-level directory structure

As you can see, I've created a separate directory structure for each of our four virtual machines. The build server needs to have CruiseControl installed, a web container (I'm using Jakarta Tomcat 5.0) for CruiseControl to publish its build results with, and a working area where to check out the projects to put on CruiseControl. The developer machines only need to have a checked out copy of the project so the "workingcopy" folder is all they need. Finally, the repository server needs to have the source control repository installed. I've used Subversion for the purposes of this article but any SCM system supported by CruiseControl would do (i.e. one of CVS, Subversion, ClearCase, MKS, Perforce, PVCS, Starteam, Visual SourceSafe).

There's one piece of software I left out. You need to have Ant installed and available for at least the build server. I've installed mine under "C:\Apps" but yours could be anywhere as long as Ant's "bin" directory is in your PATH.

The Sample Project

Something to build, right? Since this article is about setting up CruiseControl, we're going to use a very simple project with only a couple of classes, a couple of tests, and a trivial Ant build script. See the Resources section for downloading the sample project as a .zip file.

The project's directory structure is essentially as follows:

src/main Production source code. All we need is a single class, com.javaranch.journal.cruisecontrol.Calculator.
src/test Unit tests. All we need is a single JUnit TestCase, com.javaranch.journal.cruisecontrol.CalculatorTest.
lib 3rd party libraries. In our case, this includes only junit.jar.
build.xml The project's Ant script, build.xml, is located in the project's root directory.
build-cc.xml This is a "wrapper" build script for CruiseControl to use during the build cycle. It's good practice to separate the CruiseControl stuff into a separate file in order to keep the project's main build script as clean as possible. Basically, this script is called by CruiseControl to 1) update the project from the repository, and to 2) build the project. Step 2 is achieved by delegating to appropriate targets inside the project's build.xml.

The two pieces of this sample project we're really interested of are the build script and the Calculator class.

Here's the source code for the Calculator class:

package com.javaranch.journal.cruisecontrol;

/**
 * The most unimaginative class one could come up with for demonstrating
 * continuous integration.
 *
 * @author Lasse Koskela
 */
public class Calculator {

  public int add(int x, int y) {
                return x + y;
  }

  public int subtract(int x, int y) {
    return x - y;
  }
}
        

And here's the accompanying test class, CalculatorTest:

package com.javaranch.journal.cruisecontrol;

import junit.framework.TestCase;

/**
 * Just a sample test case for demonstrating how the automated build runs unit
 * tests and acts according to the results.
 *
 * @author Lasse Koskela
 */
public class CalculatorTest extends TestCase {

  private Calculator calculator;

  protected void setUp() throws Exception {
    calculator = new Calculator();
  }

  public void testAddition() throws Exception {
    assertEquals(3, calculator.add(1, 2));
  }

  public void testSubtraction() throws Exception {
    assertEquals(1, calculator.subtract(3, 2));
  }
}
        

For our purposes, this is really all we need. The fact that we have two tests will help us to experiment with how CruiseControl's Continuous Integration process reacts to failing tests (we can make one of them temporarily fail on purpose), or possible compilation errors.

Setting Up the Repository and Working Copies

Since we most definitely need a repository to put our project into, we need to do some more installing. As I mentioned earlier, I've chosen to use Subversion. The free online book about Subversion includes a nice little quick start section for getting you up and running in minutes of downloading Subversion in case you can't make sense of my admittedly brief instructions below.

Assuming you have unzipped/installed Subversion somewhere and added its "bin" directory into your PATH environment variable, these are the steps you need to do to set up the repository for our sample project (if you installed the "virtual machines" under a directory different from "C:\CIA", adjust the paths in these examples according to where you decided to place the "buildserver", "repositoryserver", etc. directories).

First, we need to create the repository itself:

C:\> svnadmin create c:\cia\repositoryserver\svnrepository

Next, we need to create our project into the repository. That's a bit trickier thing to do so follow the instructions carefully.

Create a temporary directory somewhere, e.g. "C:\tmp\project", and create the Subversion-specific directory structure as follows (don't worry about the meaning of these directories -- just think of them as something Subversion needs for doing its job).

C:\> mkdir tmp\project
C:\> cd tmp\project
C:\tmp\project> mkdir branches
C:\tmp\project> mkdir tags
C:\tmp\project> mkdir trunk
C:\Tmp\project> dir
 Volume in drive C is GWCS60
 Volume Serial Number is E472-052C

 Directory of C:\tmp\project

12.09.2004  11:53    <DIR>          .
12.09.2004  11:53    <DIR>          ..
12.09.2004  11:53    <DIR>          branches
12.09.2004  11:53    <DIR>          tags
12.09.2004  11:53    <DIR>          trunk
               0 File(s)              0 bytes
               5 Dir(s)   7?198?642?176 bytes free
C:\tmp\project>

At this point, you need to copy the project's contents under the newly created "trunk" directory as follows:

trunk\build.xml
trunk\src\...
trunk\lib\...

After creating this "skeleton" of our project, we need to import the directory structure into our repository with the following command:

C:\tmp\project> svn import C:\tmp\project file:///c:/cia/repositoryserver/svnrepository -m "Initial import"
Adding         C:\tmp\project\trunk
Adding         C:\tmp\project\trunk\lib
Adding  (bin)  C:\tmp\project\trunk\lib\junit.jar
Adding         C:\tmp\project\trunk\src
Adding         C:\tmp\project\trunk\src\test
Adding         C:\tmp\project\trunk\src\test\com
Adding         C:\tmp\project\trunk\src\test\com\javaranch
Adding         C:\tmp\project\trunk\src\test\com\javaranch\journal
Adding         C:\tmp\project\trunk\src\test\com\javaranch\journal\cruisecontrol
Adding         C:\tmp\project\trunk\src\test\com\javaranch\journal\cruisecontrol\CalculatorTest.java
Adding         C:\tmp\project\trunk\src\main
Adding         C:\tmp\project\trunk\src\main\com
Adding         C:\tmp\project\trunk\src\main\com\javaranch
Adding         C:\tmp\project\trunk\src\main\com\javaranch\journal
Adding         C:\tmp\project\trunk\src\main\com\javaranch\journal\cruisecontrol
Adding         C:\tmp\project\trunk\src\main\com\javaranch\journal\cruisecontrol\Calculator.java
Adding         C:\tmp\project\trunk\build.xml
Adding         C:\tmp\project\trunk\build-cc.xml
Adding         C:\tmp\project\branches
Adding         C:\tmp\project\tags

Committed revision 1.

C:\tmp\project>

If you're seeing output similar to the above, you've successfully imported our sample project under Subversion's control. You're now free to delete the temporary directory, "C:\tmp\project", since everything is safely stored in the repository.

Now, let's check out some working copies for our developer machines!

You check out a project from a Subversion repository using the "svn checkout" command. Here's an example of how you can check out a working copy for a "developer machine" located at "C:\Developer1":

C:\Developer1> svn checkout file:///c:/cia/repositoryserver/svnrepository/trunk workingcopy
A  workingcopy\lib
A  workingcopy\lib\junit.jar
A  workingcopy\src
A  workingcopy\src\test
A  workingcopy\src\test\com
A  workingcopy\src\test\com\javaranch
A  workingcopy\src\test\com\javaranch\journal
A  workingcopy\src\test\com\javaranch\journal\cruisecontrol
A  workingcopy\src\test\com\javaranch\journal\cruisecontrol\CalculatorTest.java
A  workingcopy\src\main
A  workingcopy\src\main\com
A  workingcopy\src\main\com\javaranch
A  workingcopy\src\main\com\javaranch\journal
A  workingcopy\src\main\com\javaranch\journal\cruisecontrol
A  workingcopy\src\main\com\javaranch\journal\cruisecontrol\Calculator.java
A  workingcopy\build.xml
A  workingcopy\build-cc.xml
Checked out revision 1.

C:\Developer1>

Well that wasn't too difficult, was it? Repeat the "svn checkout" command for the second developer machine.

Now that we're on the roll with Subversion checkouts, why not create a working copy for our build server as well:

C:\BuildServer\WorkArea> svn checkout file:///c:/cia/repositoryserver/svnrepository/trunk SampleCCProject
A  SampleCCProject\lib
A  SampleCCProject\lib\junit.jar
A  SampleCCProject\src
A  SampleCCProject\src\test
A  SampleCCProject\src\test\com
A  SampleCCProject\src\test\com\javaranch
A  SampleCCProject\src\test\com\javaranch\journal
A  SampleCCProject\src\test\com\javaranch\journal\cruisecontrol
A  SampleCCProject\src\test\com\javaranch\journal\cruisecontrol\CalculatorTest.java
A  SampleCCProject\src\main
A  SampleCCProject\src\main\com
A  SampleCCProject\src\main\com\javaranch
A  SampleCCProject\src\main\com\javaranch\journal
A  SampleCCProject\src\main\com\javaranch\journal\cruisecontrol
A  SampleCCProject\src\main\com\javaranch\journal\cruisecontrol\Calculator.java
A  SampleCCProject\build.xml
A  SampleCCProject\build-cc.xml
Checked out revision 1.

C:\BuildServer\WorkArea>

This is the project checkout CruiseControl will later use to synchronize with the repository and do the automated builds with. Note that I've used a different name for the checkout directory than with the developer machines. The reason for doing this is that while it doesn't really matter what the developer decides to name his working copy, it is a good idea to keep CruiseControl's working area clean by using descriptive names -- if you're running multiple projects on CruiseControl and they're named "project1", "project2", and so on, you're bound to have trouble with your CruiseControl directories full of meaningless names and your CruiseControl configuration file extra-vulnerable to copy-paste errors.

All set? Good. We've now got a project on the repository server and a couple of developers hooked up. All we need now is the build server which we're going to set up next!

Getting, Installing and Building CruiseControl

CruiseControl downloads are available via SourceForge.net. I've written this article based on version 2.1.6 of CruiseControl but you should be able to follow through with a slightly older or newer version if you're not afraid of hacking your way through possible differences in the configuration file syntax, etc.

Once you've downloaded cruisecontrol-2.1.6.zip somewhere on your hard drive, unzip its contents into our build server directory as follows:

The CruiseControl directory

If you're wondering what those directories inside the CruiseControl distribution are, you're in luck, because we're just about to explore them a bit further.

The "docs" directory, as you might've guessed by now, contains some CruiseControl documentation in HTML format. These documents are not really essential, however, since the really useful documentation is inside the other two directories, namely "main/docs" and "reporting/jsp/docs". Feel free to take a peek but you shouldn't need to look at them in order to get started with CruiseControl. That is, assuming I'm doing a good job explaining the tricks of the trade in this article...

So, next up is the "main" directory. This directory contains the CruiseControl "engine", along with all source code, tests and build scripts for building it from a clean slate. The "main" directory contains too many directories to warrant a detailed description for each of them so I'll just mention those you need to know about.
main/bin This directory contains a batch/shell script for launching the CruiseControl process, i.e. cruisecontrol.bat or cruisecontrol.sh depending on your operating system.
main/dist This directory contains the cruisecontrol.jar, i.e. the CruiseControl engine we're going to launch in just a few more minutes. If you can't see such a file, back up to the "main" directory and run "ant jar" to compile CruiseControl and to create the cruisecontrol.jar under "dist".
main/logs This is the directory where we're going to make CruiseControl collect all the historical build results for each project. Well, since we're only going to put one project on CruiseControl, there will be just one subdirectory under "logs", namely "SampleCCProject".

All clear? So what's the remaining "reporting" directory next to "main"? Right now, I'll just say it contains the J2EE web application used for reporting CruiseControl build results online after each automated build. We'll get back to it in Part 2 of this tutorial.

Oh, did I mention that you've now "installed" CruiseControl? Well, you have. Now we just need to configure it to build our sample project...

Configuring CruiseControl

In order for CruiseControl to be able to do anything sensible, you need to provide a configuration file telling which projects to build and, most importantly, how.

This configuration file, which is an XML document, is traditionally named "config.xml" although you can use a different name using the "-configfile" command-line argument for the cruisecontrol.bat/.sh script to point to your custom named configuration.

Instead of going through the CruiseControl config.xml "DTD" (there's not a real DTD document available, just an informal HTML page describing each allowed element), I'll just throw the file we're going to use at you and explain what each of the elements we're using mean and how you could tweak them for slightly different behavior or for a different environment. For more details about the possibilities provided by the CruiseControl configuration file, please refer to the online documentation for config.xml.

One more thing before diving in, though, and something you might want to keep in mind while going through the different configurations. The goal behind all this work is to get timely feedback to the development team about the status of the code in our source repository. If the configuration currently in version control doesn't build at all, stumbles on a missing library, or doesn't pass its unit tests, we want to let the developers know. We want this feedback on two levels: high and low. The high-level feedback is a binary answer to the question "is the build ok?" The low-level feedback is the possibility for a developer to dig in deeper to figure out what exactly went wrong with the build that turned out "red".

Without further delay, the config.xml for our sample project:

<?xml version="1.0"?>
<cruisecontrol>
  <project name="SampleCCProject">
    <bootstrappers>
      <currentbuildstatusbootstrapper
        file="../logs/currentbuild.txt" />
      <svnbootstrapper file="build-cc.xml"
        localWorkingCopy="../../../WorkArea/SampleCCProject" />
    </bootstrappers>
    <modificationset quietperiod="60" >
      <svn LocalWorkingCopy="../../../WorkArea/SampleCCProject"/>
    </modificationset>
    <schedule interval="60" >
      <ant antWorkingDir="../../../WorkArea/SampleCCProject"
        buildfile="build-cc.xml" />
    </schedule>
    <log dir="../logs/SampleCCProject">
      <merge dir="../../../WorkArea/SampleCCProject/reports/junit/data"/>
    </log>
    <publishers>
      <currentbuildstatuspublisher
        file="../logs/currentbuild.txt" />
      <artifactspublisher dir="../../../WorkArea/SampleCCProject/dist"
        dest="../logs/SampleCCProject" />
      <email mailhost="smtp.yourdomain.com"
        returnaddress="buildmaster@yourdomain.com"
        skipusers="true"
        reportsuccess="fixes"
        subjectprefix="[CruiseControl]"
        buildresultsurl="http://buildserver:8080/cruisecontrol/buildresults">
        <failure address="developers@yourdomain.com" />
        <success address="developers@yourdomain.com" />
      </email>
    </publishers>
  </project>
</cruisecontrol>
<cruisecontrol>

The root element of the configuration file, <cruisecontrol>, doesn't take any attributes. In fact, it only accepts two kinds of child attributes and that's all. The <cruisecontrol> element can have 1 or more <project> child elements for configuring one or more projects to put under CruiseControl's surveillance. In our case, we only have one project to play with so we're using only one <project> configuration, the "SampleCCProject". You can also register arbitrary plugins for CruiseControl with the <plugin> child element. We're not going to use any plugins for our simple example project so I'll leave that for a future article.

<project>

The <project> element is where you tell CruiseControl what to build, when to build, how to build, and how to report. The <project> element accepts two attributes. The name attribute specifies a unique (within the configuration file) name for the project configuration and is required. The optional buildafterfailed attribute can be used to tell CruiseControl whether it should continue trying to build the project after a failed build even though no commits have been detected in the repository since the last build. The default value for buildafterfailed is 'true', which means CruiseControl will keep on trying to do a build after one that failed, even though no further modifications would be made into the repository.

Now, a <project> element has plenty of possible child elements, namely <bootstrappers>, <modificationset>, <schedule>, <log>, <publishers>, <dateformat> and <plugin>. Out of these, only <modificationset> and <schedule> are strictly required, but in practice you'll pretty much always going to use the five listed in our example.

<bootstrappers>

The <bootstrappers> element can be used to list bootstrappers, which are executed before the actual build attempt. Possibly the most used bootstrapper is the <currentbuildstatusbootstrapper> also present in our example, which writes a "current build started at ..." entry into the currentbuild.txt file informing external applications (such as the reporting web application I already mentioned in passing) about a build being in progress. Another typical use of bootstrappers is to update a specific file from a repository prior to the build -- just like what our configuration is doing for the build-cc.xml wrapper build script with the help of <svnbootstrapper>. This is useful for two reasons: 1) because your wrapper script might end up being out-of-synch with the project, effectively skewing the build results, and 2) because it's nice to keep the project's main build script as lean as possible.

<modificationset>

The <modificationset> element is where you specify how CruiseControl should figure out whether a build is needed, i.e. whether someone has committed changes into the repository (or repositories if you've split your project into several version control systems). The <modificationset> recognizes two attributes, both of which are optional. The first attribute is requiremodification, which tells CruiseControl whether it should do a build even if there are no modifications in the repository since the last build. It defaults to 'true'. If you want a "nightly build" regardless of whether anyone has checked in since the last build, set this to 'false'. The other attribute is quietperiod, which represents the duration (in seconds) CruiseControl should wait to make sure that it doesn't start doing a build while someone is checking in his changes. In other words, CruiseControl waits until there's a long enough period of silence in the repository before proceeding with the build. The default value for 'quietperiod' is '60', which should be just fine for most projects, including ours.

In our example, we're using the <svn> modificationset task to check whether any changes have been committed to the project associated with our working copy. Under the hood, the <svn> task performs a 'svn log' command to get a list of changes between the last build date and the start of the current build cycle.

Most projects will suffice with the SCM-spesific task checking for changes in a repository, but that's not quite all you can do with <modificationset>. You see, there's also a nice little task named <buildstatus> that you can use to trigger your build whenever another CruiseControlled project has had a successful build. Again, take a look at the online documentation if you're interested in the nitty-gritty details.

<schedule>

We've already seen how to tell CruiseControl the 'what' and part of 'when'. The <schedule> element is the latter half of the 'when' as this is the element which specifies how frequent you want your build cycle to start. <schedule> takes just one attribute, interval, which represents a duration in seconds that CruiseControl will wait between build attempts. The interval defaults to '300' (five minutes) which should be quite OK for most projects. However, since we're in the learning mode, we'll set the interval to just 60 seconds so we don't have to wait for too long after making experimental changes (intentional compilation errors, failing tests, that sort of stuff).

The children of <schedule> are called builders and are used for specifying the 'how' part of our configuration. The built-in builders provided out of the box, <ant> and <maven>, can be used to launch Ant and Maven build scripts, respectively.

The <ant> builder used in our example tells CruiseControl to use a build script named "build-cc.xml" (the default would be "build.xml") in the specified working copy of our project, and to execute the default target, whatever that is (in our case, it's "build"). If you don't want to rely on the build script's default target, (for example when you're using the project's main build script directly without any wrappers) you can specify the target(s) you want to execute by using the target attribute. In addition to these common attributes, <ant> provides the option to specify all sorts of extra properties for the builder like setting a timeout for the build script. Once again, refer to the documentation for the specifics. One specific attribute I'd like to mention here, however.

The multiple attribute can be used to tell CruiseControl to execute the builder in question only every nth time. This comes in handy if you have a huge project taking a long time to compile from scratch (a "clean build" in the geek slang). In those cases, it's often a good idea to specify two separate <ant> builders to employ incremental compilation; one to execute the project's build script's "clean" target every 5th (for example) time and another to execute the project's "build" target every time. Here's an example of such a setup:

<schedule interval="60" >
  <ant antWorkingDir="../../../WorkArea/SampleCCProject" target="clean" multiple="5"/>
  <ant antWorkingDir="../../../WorkArea/SampleCCProject" target="build" multiple="1"/>
</schedule>

I lied. In fact, the <schedule> element can indeed accommodate one non-builder task. Well, technically it is a builder since it extends the same class as net.sourceforge.cruisecontrol.builders.AntBuilder and net.sourceforge.cruisecontrol.builders.MavenBuilder but semantically it's not really a builder per se. This element is <pause> and it can be used to tell CruiseControl not to do a build during a specified time window. Some of the common uses for the <pause> element would be to prevent any builds from executing during the weekend or during a nightly backup of a critical resource.

<log>

Alright. We've now gotten all modifications from the repository and executed an automated build (if deemed necessary). What's left to do is to interpret the results and publish them for humans to see. The <log> element is the tool to do the former.

The <log> element accepts an attribute named dir, which must point to the directory CruiseControl will aggregate the build history for this particular project. In our example, this directory happens to be "../logs/SampleCCProject" (relative to the "main/bin" directory where we executed the cruisecontrol.bat/.sh script from). The <log> element also accepts an encoding attribute to allow the use of non-default encoding for the log files.

At this point, we know that all those log files are going to the directory specified by the dir attribute. Now how do those compilation errors and unit test results end up into CruiseControl's log files? The answer lies in the only child elements recognized by <log>, the aptly named <merge>.

<merge> is a task which merges the resulting log files from running the Ant (or Maven) scripts during the build. Let's go through the element's accepted attributes and then talk a bit about what happens behind the curtains in this mysterious merge operation.

The <merge> element can be told to merge either a specified XML file or all XML files (having the ".xml" suffix) under the specified directory. These attributes are named "file" and "dir", respectively.

"All XML files?" you ask. Yes, all XML files. The way CruiseControl works is that it wire-taps the build script recording any output into one huge log file and lets the reporting applications worry about picking the information they want out of the mammoth log file. Once we've got CruiseControl running, you can take a peek at the log files under "BuildServer\CruiseControl\main\logs\SampleCCProject" to see for yourself what information is CruiseControl harvesting from the project's build process.

<publishers>

The <publishers> element encapsulates actions that should happen after a build has been attempted. Remembering the goal of having a Continuous Integration server, their responsibility is rather obvious -- to publish the build results to interested parties in some meaningful way. For most projects, this "meaningful way" is probably a combination of email and web pages, but other channels of communication can be used as well. I'll first introduce some of the built-in publishing tasks and then give a brief mention about other, perhaps more extreme ways to publish build results from CruiseControl.

Probably the most common publisher is the <currentbuildstatuspublisher> which can be used to notify external applications -- such as the CruiseControl reporting web application -- about the latest build results being available. You just need to specify the filename for the task to write its "build completed" message to as we've done in our example:

<currentbuildstatuspublisher file="../logs/currentbuild.txt"/>

There's also an FTP version named <currentbuildstatusftppublisher/> if you have a setup consisting of multiple CruiseControl servers or for some other reason wish to update the current build status file on a machine other than the CruiseControl server you're configuring. Similarly, there's <ftppublisher> for copying the whole build results log file over to another server and <scp> for copying an arbitrary file over an SSH connection.

I already mentioned that many projects tend to use email as a channel for publishing their build results. CruiseControl supports this with two types of email publishers: the "link email publisher" (<email>) and the "HTML email publisher" (<htmlemail>). The former is used to send an email with just one link to the build results page somewhere accessible by the team (typically the CruiseControl reporting web application). The latter, however, takes a radically different approach and embeds all the build results information into the very email being sent to all configured recipients.

I've included the regular <email> publisher into our sample application to give you an idea of how the email integration works. Note that you must have access to an SMTP server to be able to send the build results emails. If you don't have that access, just comment out this particular publisher and you're good to go.

Here's the <email> publisher once again:

<email mailhost="smtp.yourdomain.com"
       returnaddress="buildmaster@yourdomain.com"
       skipusers="true"
       reportsuccess="fixes"
       subjectprefix="[CruiseControl]"
       buildresultsurl="http://buildserver:8080/cruisecontrol/buildresults">
        <failure address="developers@yourdomain.com" />
        <success address="developers@yourdomain.com" />
</email>

What the attributes of the <email> element tell us is that the SMTP server can be found at smtp.yourdomain.com, the sender address (and return address) CruiseControl should use is "buildmaster@yourdomain.com", CruiseControl should skip sending the email to those users who had committed changes since the last build, CruiseControl should only send an email after a successful build if it was previously failing (the other alternatives are "never" and "always" which I doubt need any explanation) , the email being sent should have the given prefix in the subject line (to help in filtering/recognizing CruiseControl's emails), and that the build results are available at the given URL (we'll come back to this when setting up the reporting web application in the next installment of this tutorial!).

These are not all the attributes you can specify, however. There are plenty more, including defaultsuffix with which you can specify the default domain name for recipient addresses picked up from the SCM, failasimportant with which you can put the red "important" flag up in the developers' mail client in case a build failed, a username and password in case you need to authenticate to the mail server, returnname which you typically use to give your CruiseControl server a little personality, and spamwhilebroken which CruiseControl uses to decide whether it should continue sending "build failed" emails for subsequent failing builds.

Again, the full potential available to you is revealed in the official documentation.

Now what about those child elements? Our little example is using two very similar child elements for <email>, namely <failure> and <success>. The <email> (and <htmlemail>) element can include as many of these as you like and their purpose is to tell CruiseControl who it should send email to in case of a failed and a successful build, respectively. There's also a third similar child element named <always>, which combines the two specifying a recipient that should receive emails on both successes and failures.

Finally, the <email> element supports the <map> child element which you can use to map the usernames in your version control system (e.g. "lkoskela") into email addresses (e.g. "lasse.koskela@yourdomain.com") as follows:

<map alias="lkoskela" address="lasse.koskela@yourdomain.com"/>

The "fat" version of the email publisher, <htmlemail>, adds a few more attributes you can (or must) specify. First of all, you must point CruiseControl to the log directory where all the build results for your project are collected. This is done with the logdir attribute. The rest of the additional attributes are related to formatting the HTML email.

If you want to specify a single XSL file for transforming the CruiseControl build results into a nice little HTML document, you point to the stylesheet using the xslfile attribute. Alternatively, if you don't feel like writing such a monster for a stylesheet, you can fall back on the standard stylesheets by pointing the xsldir attribute to the "xsl" directory holding the XSL files that came as part of the CruiseControl distribution and by pointing the css attribute to the CSS stylesheet in the adjacent "css" directory. Of course, you can tweak these XSL and CSS documents as you please!

If you feel like trying out the HTML email publisher, replace the earlier <email> configuration with this <htmlemail> version:

<htmlemail mailhost="smtp.yourdomain.com"
          returnaddress="buildmaster@yourdomain.com"
          skipusers="true"
          reportsuccess="fixes"
          subjectprefix="[CruiseControl]"
          buildresultsurl="http://buildserver:8080/cruisecontrol/buildresults"
          logdir="C:\CIA\BuildServer\CruiseControl\main\logs\SampleCCProject"
          xsldir="C:\CIA\BuildServer\CruiseControl\reporting\jsp\xsl"
          css="C:\CIA\BuildServer\CruiseControl\reporting\jsp\css\cruisecontrol.css">
  <failure address="developers@yourdomain.com" />
  <success address="developers@yourdomain.com" />
</htmlemail>

Changing the primitive link email publisher configuration to the fancier HTML version above produces the following type of an email after intentionally making our unit test fail:

A sample 'build failed' email using the HTML email publisher

Approximately 1 minute and 22 seconds later, after fixing that failing test and committing my changes to the repository, I got the following "build successful" email:

A sample 'build complete' email using the HTML email publisher

Quite nice -- and useful -- isn't it?

By tweaking the XSL stylesheets you can do pretty much customization regarding what the email being sent out contains. However, sometimes you'd like to make certain by-products of the build available along with the build results. In those situations, and as I've done in our example, the <artifactspublisher> can be used to archive build artifacts such as JAR files, javadocs, etc. somewhere for safekeeping. In the example, the below configuration copies everything from the "dist" directory of our project under the particular project's CruiseControl log directory:

<artifactspublisher dir="../../../WorkArea/SampleCCProject/dist"
                    dest="../logs/SampleCCProject" />

To be more accurate, the files under "dist" end up into a subdirectory named after the build timestamp, e.g. "logs/SampleCCProject/20040914190929". The artifacts archived as part of the build history can be made available for later retrieval or viewing through the reporting web application, for example.

If none of the above publishers quite satisfy your need to publicize, you can always plug in your own implementation or delegate to an external program using the built-in <execute> publisher. Talking of which, I believe I promised to talk a bit about the more exotic implementations of a CruiseControl publisher, didn't I?

Perhaps one of the most famous custom CruiseControl publishers to date has been the quite innovative application of lava lamps and X10 automation electronics. What makes such a configuration really cool isn't the fact that someone has gone through the trouble of hooking up these seemingly remote objects -- a CruiseControl server and two lava lamps -- but how the team in question has made use of the "unused bandwidth" of their environment, the gentle visual signal of the red lava lamp slowly starting to bubble when someone has broken the build.

Another clever, although far from radical, way to publish build results is to install an always-on client-side widget -- usually a system tray application -- that shows the green/red state of the current build and possibly informs the developers when the next build is due.

I know of a couple of such implementations, namely Ivan Moore's Python script which screen-scrapes the CruiseControl reporting web application to figure out build status and Dashboard which consists of a server-side component that hooks up to CruiseControl's AntBuilder and broadcasts build events to IDE plugins (currently only Eclipse and IDEA) within the local area network over multicast.

There are similar utilities for other Continuous Integration products as well CruiseControl and it shouldn't be too difficult to implement your own with some spare time and willingness to dive into CruiseControl's source code.

That's about enough talking. It's time to see our carefully crafted CruiseControl configuration in action!

3, 2, 1, ... INTEGRATE!

With the configuration file in place under "main/bin", you can finally launch CruiseControl with the following command:

C:\CruiseControl\main\bin> cruisecontrol

The output should look like this:

C:\CIA\BuildServer\CruiseControl\main\bin>cruisecontrol
"C:\java\j2se\bin\java" -cp "..." CruiseControl
[cc]syys-14 20:42:31 Main          - CruiseControl Version 2.1 Compiled on September 12 2004 1552
[cc]syys-14 20:42:31 trolController- projectName = [SampleCCProject]
[cc]syys-14 20:42:31 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:42:31 Project       - Project SampleCCProject starting
[cc]syys-14 20:42:31 Project       - Project SampleCCProject:  idle
[cc]syys-14 20:42:31 BuildQueue    - BuildQueue started
[cc]syys-14 20:42:31 Project       - Project SampleCCProject started
[cc]syys-14 20:42:31 Project       - Project SampleCCProject:  next build in 1 minutes

After a few minutes, you should be looking at an output similar to mine:

[cc]syys-14 20:46:11 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 20:46:11 BuildQueue    - now building: SampleCCProject
[cc]syys-14 20:46:11 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:46:11 Project       - Project SampleCCProject:  bootstrapping
At revision 39.
[cc]syys-14 20:46:13 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 20:46:14 Project       - Project SampleCCProject:  No modifications found, build not necessary.
[cc]syys-14 20:46:14 Project       - Project SampleCCProject:  idle
[cc]syys-14 20:46:14 Project       - Project SampleCCProject:  next build in 1 minutes
[cc]syys-14 20:47:14 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 20:47:14 BuildQueue    - now building: SampleCCProject
[cc]syys-14 20:47:14 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:47:14 Project       - Project SampleCCProject:  bootstrapping
At revision 39.
[cc]syys-14 20:47:16 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 20:47:17 Project       - Project SampleCCProject:  No modifications found, build not necessary.
[cc]syys-14 20:47:17 Project       - Project SampleCCProject:  idle
[cc]syys-14 20:47:17 Project       - Project SampleCCProject:  next build in 1 minutes
[cc]syys-14 20:48:17 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 20:48:22 BuildQueue    - now building: SampleCCProject
[cc]syys-14 20:48:22 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:48:22 Project       - Project SampleCCProject:  bootstrapping
At revision 39.
[cc]syys-14 20:48:24 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 20:48:25 Project       - Project SampleCCProject:  No modifications found, build not necessary.
[cc]syys-14 20:48:25 Project       - Project SampleCCProject:  idle
[cc]syys-14 20:48:25 Project       - Project SampleCCProject:  next build in 1 minutes

Notice how the same loop repeats time and time again? That's CruiseControl idling because nobody has checked in anything since the last build. Why don't we get some action by introducing some build failures...

Edit src\main\com\javaranch\journal\cruisecontrol\Calculator.java in one of the developer machines' working directory by changing the subtract() method from:

  public int subtract(int x, int y) {
    return (x - y);
  }

to:

  public int subtract(int x, int y) {
    return (x - y) + 5;
  }

Then, commit your intentional build-breaker to the repository with

C:\Developer1\workingcopy> svn commit . -m "I did it"
Sending        src\main\com\javaranch\journal\cruisecontrol\Calculator.java
Transmitting file data .
Committed revision 40.

C:\Developer1\workingcopy>

After a couple of minutes of anxious waiting, you should see output similar to the following:

[cc]syys-14 20:58:54 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 20:58:54 BuildQueue    - now building: SampleCCProject
[cc]syys-14 20:58:54 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:58:54 Project       - Project SampleCCProject:  bootstrapping
At revision 39.
[cc]syys-14 20:58:56 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 20:58:57 Project       - Project SampleCCProject:  No modifications found, build not necessary.
[cc]syys-14 20:58:57 Project       - Project SampleCCProject:  idle
[cc]syys-14 20:58:57 Project       - Project SampleCCProject:  next build in 1 minutes
[cc]syys-14 20:59:57 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 20:59:57 BuildQueue    - now building: SampleCCProject
[cc]syys-14 20:59:57 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 20:59:57 Project       - Project SampleCCProject:  bootstrapping
At revision 40.
[cc]syys-14 20:59:59 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 21:00:00 odificationSet- 1 modification has been detected.
[cc]syys-14 21:00:00 odificationSet- A modification has been detected in the quiet period.
[cc]syys-14 21:00:00 odificationSet- Sleeping for 49 seconds before retrying.
[cc]syys-14 21:00:51 odificationSet- 1 modification has been detected.
[cc]syys-14 21:00:51 odificationSet- A modification has been detected in the quiet period.
[cc]syys-14 21:00:51 odificationSet- Sleeping for 0 seconds before retrying.
[cc]syys-14 21:00:52 odificationSet- 1 modification has been detected.
[cc]syys-14 21:00:52 Project       - Project SampleCCProject:  now building
Buildfile: build-cc.xml

update:
     [exec] U  src\main\com\javaranch\journal\cruisecontrol\Calculator.java
     [exec] Updated to revision 40.

build:

setup.properties:

setup.paths:

setup:

compile.main:
    [javac] Compiling 1 source file to C:\CIA\BuildServer\WorkArea\SampleCCProject\classes\main

compile.tests:

compile:

jar:
      [jar] Building jar: C:\CIA\BuildServer\WorkArea\SampleCCProject\dist\sample.jar

test:
   [delete] Deleting directory C:\CIA\BuildServer\WorkArea\SampleCCProject\reports\junit\data
    [mkdir] Created dir: C:\CIA\BuildServer\WorkArea\SampleCCProject\reports\junit\data
    [junit] Running com.javaranch.journal.cruisecontrol.CalculatorTest
    [junit] Tests run: 2, Failures: 1, Errors: 0, Time elapsed: 0,05 sec
    [junit] TEST com.javaranch.journal.cruisecontrol.CalculatorTest FAILED

BUILD FAILED
file:C:/CIA/BuildServer/WorkArea/SampleCCProject/build.xml:67: Some unit tests failed

Total time: 5 seconds
[cc]syys-14 21:00:57 Project       - Project SampleCCProject:  merging accumulated log files
[cc]syys-14 21:00:57 Project       - Project SampleCCProject:  publishing build results
[cc]syys-14 21:00:58 EmailPublisher- Sending mail notifications.
[cc]syys-14 21:01:02 Project       - Project SampleCCProject:  idle
[cc]syys-14 21:01:02 Project       - Project SampleCCProject:  next build in 1 minutes

Notice how at 21:00:00 CruiseControl's build loop detects our modification in the repository, waits for a short, random time after detecting that a modification (the same one...) has happened during the quiet period (which we specified to be 60 seconds), and finally proceeds by executing our build-cc.xml wrapper Ant script.

You can see from the output how build-cc.xml invokes the project's main build script, how the CalculatorTest fails, and how CruiseControl processes the build results by sending email, finally going back to sleep for the specified build interval (again, 60 seconds in our example).

Also notice that CruiseControl keeps on doing builds every 60 seconds even though no further modifications have been detected in the repository -- this behavior is due to the fact that we omitted the buildafterfailed attribute from our configuration file's <project> element, effectively letting it default to 'true'. If we had set it to 'false', CruiseControl would sit still until someone commits something into the repository.

So, now that we know CruiseControl detects our failures, let's see whether it detects our fixes... Edit the Calculator class again, returning the subtract() method to its valid implementation, and commit your changes with the same command as before -- except for the commit comment, of course, which should indicate that our edit was to fix the defect we introduced earlier:

C:\Developer1\workingcopy> svn commit . -m "I fixed it"
Sending        src\main\com\javaranch\journal\cruisecontrol\Calculator.java
Transmitting file data .
Committed revision 41.

C:\Developer1\workingcopy>

Soon, CruiseControl should again pick up our commit from the repository and, hopefully, verify that our "fix" was correct:

[cc]syys-14 21:16:54 Project       - Project SampleCCProject:  in build queue
[cc]syys-14 21:16:54 BuildQueue    - now building: SampleCCProject
[cc]syys-14 21:16:54 Project       - Project SampleCCProject:  reading settings from config file
  [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml]
[cc]syys-14 21:16:54 Project       - Project SampleCCProject:  bootstrapping
At revision 41.
[cc]syys-14 21:16:57 Project       - Project SampleCCProject:  checking for modifications
[cc]syys-14 21:16:58 odificationSet- 4 modifications have been detected.
[cc]syys-14 21:16:58 odificationSet- A modification has been detected in the quiet period.
[cc]syys-14 21:16:58 odificationSet- Sleeping for 30 seconds before retrying.
[cc]syys-14 21:17:30 odificationSet- 4 modifications have been detected.
[cc]syys-14 21:17:30 Project       - Project SampleCCProject:  now building
Buildfile: build-cc.xml

update:
     [exec] U  src\main\com\javaranch\journal\cruisecontrol\Calculator.java
     [exec] Updated to revision 41.

build:

setup.properties:

setup.paths:

setup:

compile.main:
    [javac] Compiling 1 source file to C:\CIA\BuildServer\WorkArea\SampleCCProject\classes\main

compile.tests:

compile:

jar:
      [jar] Building jar: C:\CIA\BuildServer\WorkArea\SampleCCProject\dist\sample.jar

test:
   [delete] Deleting directory C:\CIA\BuildServer\WorkArea\SampleCCProject\reports\junit\data
    [mkdir] Created dir: C:\CIA\BuildServer\WorkArea\SampleCCProject\reports\junit\data
    [junit] Running com.javaranch.journal.cruisecontrol.CalculatorTest
    [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0,04 sec

all:

BUILD SUCCESSFUL
Total time: 4 seconds
[cc]syys-14 21:17:34 Project       - Project SampleCCProject:  merging accumulated log files
[cc]syys-14 21:17:34 Project       - Project SampleCCProject:  publishing build results
[cc]syys-14 21:17:35 EmailPublisher- Sending mail notifications.
[cc]syys-14 21:17:36 Project       - Project SampleCCProject:  idle
[cc]syys-14 21:17:36 Project       - Project SampleCCProject:  next build in 1 minutes

Alright! We've got a Continuous Integration server up and running, responding timely and correctly to our human errors and the resulting corrective actions!

If you had the <email> or <htmlemail> publisher configured properly while playing around with Calculator.java, you should have a couple of emails from CruiseControl in your mailbox right now. If you decided to comment out the email stuff, don't worry, because the next step is to set up the reporting web application -- in Part 2...


Resources

A .zip file of the sample project.

Discuss this article in The Big Moose Saloon!


Return to Top
Java Designs
Examining Layouts with Swing
by Tom Tolman
When you first add a number of components to your Java user interface, you may find yourself asking: Where did some of them go? On other occasions, you may find yourself adding a lot of widgets to your container only to find them crawling around like rascally kittens during resizing. It is because of these vexing matters we turn to layout managers: Everything can go in its proper place once you know where that place is.

It is difficult to maintain code and dangerous for long term usability when you utilize components at specific absolute locations. Different LookAndFeel packages may display components in unusable ways with hard coded size and positions. If someone were to change the wording on one of the labels of your interface, the entire look would become disjointed. Layout managers allow us to create code which, can easily be upgraded and utilized on different displays.

You can define your own layout managers, and you should do so for large applications demanding a consistent look, but for the purposes of this article we are going to look at utilizing some of the standard layout managers.

FlowLayout This manager places items left to right and slides the components to the next line when it runs out of horizontal space. This manager is not suitable for anything besides simple dialogs because of the uncertainty of final placement. JPanel uses FlowLayout as the default layout manager.

GridLayout This manager organizes all of its elements in a rectangular grid with equally sized cells. GridLayout will resize everything it can get its hands on to equally sized areas. Components can be separated with default spacing as specified in the call to the constructor.

BorderLayout This layout has NORTH, SOUTH, EAST, WEST, and CENTER regions. You can optionally use any subset of these regions making it a powerful organizer of relative elements. This is the most commonly used layout manager for straightforward relative positioning of components.

BoxLayout A perfect layout manager for organizing elements either vertically or horizontally, as designated by the constructor. Any series of components would be well served by utilizing this manager. Unlike the GridLayout, vertically arranged components are not resized horizontally, and vice versa.

The CardLayout is extremely useful for flipping between visible sets of components, and the GridBagLayout is tailored for components spanning multiple cells of a grid. SpringLayout is appropriate for specifying the corner relationships of many components, but the four layout managers above offer enough strength for complex user interfaces.

The First Secret

The first secret to working with the standard Layout managers is to utilize JPanel. Instead of looking at the screen as a number of widgets which belong at specific positions, imagine the screen as a number of organized sections, each of which is broken down into further organized sections. Each section is a JPanel.

In the design of your interface you should break down your initial paper sketch of your display into groups of associated components. If two components belong together both logically and physically they should be organized by a layout manager within a JPanel. Organizing your components into logical groups also makes the overall user interface easier to navigate.

The example below uses nested JPanels to control layout. Observe that there are two JPanels used for horizontal placement, and a single main JPanel into which both are placed.

This is an example of nested JPanels

Source Code
import javax.swing.*;

public class NestedExample extends JFrame
{
  public NestedExample()
  {
    super("Nested Panels");
    setSize(20080);

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));

    mainPanel.add(createTopRowPanel());
    mainPanel.add(createBottomRowPanel());

    getContentPane().add(mainPanel);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }

  private JPanel createTopRowPanel()
  {
    JPanel topRowPanel = new JPanel();
    topRowPanel.setLayout(new BoxLayout(topRowPanel, BoxLayout.X_AXIS));
    topRowPanel.add(new JLabel("First:"));
    topRowPanel.add(new JTextField());
    return topRowPanel;
  }

  private JPanel createBottomRowPanel()
  {
    JPanel bottomRowPanel = new JPanel();
    bottomRowPanel.setLayout(new BoxLayout(bottomRowPanel, BoxLayout.X_AXIS));
    bottomRowPanel.add(new JLabel("Last:"));
    bottomRowPanel.add(new JTextField());
    return bottomRowPanel;
  }

  public static void main(String[] argv)
  {
// For thread safety this should be utilized in the dispatch thread
    javax.swing.SwingUtilities.invokeLater(new Runnable()
    // Anonymous class
      public void run()
      {
        NestedExample example = new NestedExample();
      }
    });
  }
}

In the above example the first name label and first name text entry are both added into a JPanel named topRowPanel with an horizontal BoxLayout manager. This places them side by side, left to right. Similarly the last name label and last name text entry are added into a JPanel named bottomRowPanel also with an horizontal BoxLayout. These two rows are then added into the mainPanel with a vertical BoxLayout which places one above the other. Utilizing different layout managers at different levels allows for sophisticated relative positioning.

The Second Secret

The second secret of using the standard layout managers is to exploit the constraints each provide you.

FlowLayout This layout evenly spaces what it contains, so can be utilized as a centering tool for a single Component as an alternative to using the BoxLayout.

GridLayout In addition to allowing you to lay out components in a grid, it resizes components so they all match size. When you see your layout demanding a number of equally sized components, the GridLayout should be pulled out of your bag of tricks.

BorderLayout The north and south regions are given their preferred height and the east and west regions are given their preferred width. The center resizes based on available space. Thus, one can utilize just the north and center regions of this layout to have a fixed top label with an adjustable sizing text entry area beneath it.

BoxLayout Invisible filler components can be added to control spacing with this layout manager. This allows one to vertically center, horizontally center, or otherwise adjust the white space around components.

The example below uses some of these constraints to manipulate the layout. The main panel is a two by two GridLayout with four JPanels added to it. Each quadrant demonstrates a different usage of layout managers to achieve specific effects.

This is an example of nested JPanels

Source Code
import javax.swing.*;
import java.awt.*;

public class ConstraintExample extends JFrame
{
  public ConstraintExample()
  {
    super("Constraint Example");
    setSize(300200);

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new GridLayout(2255));

    mainPanel.add(createNorthWestPanel());
    mainPanel.add(createNorthEastPanel());
    mainPanel.add(createSouthWestPanel());
    mainPanel.add(createSouthEastPanel());

    getContentPane().add(mainPanel);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }

  private JPanel createNorthWestPanel()
  {
    JPanel northWestPanel = new JPanel();
    northWestPanel.setLayout(new BorderLayout());
    JPanel rowPanel = new JPanel();
    rowPanel.setLayout(new BoxLayout(rowPanel, BoxLayout.X_AXIS));
    rowPanel.add(new JLabel("Nice height:"));
    rowPanel.add(new JTextField());
    northWestPanel.add(rowPanel, BorderLayout.NORTH);
    return northWestPanel;
  }

  private JPanel createNorthEastPanel()
  {
    JPanel northEastPanel = new JPanel();
    northEastPanel.setLayout(new BoxLayout(northEastPanel, BoxLayout.Y_AXIS));
    northEastPanel.add(Box.createVerticalGlue());
    northEastPanel.add(new JButton("Nice Vertical"));
    northEastPanel.add(Box.createVerticalStrut(8));
    northEastPanel.add(new JButton("Spacing"));
    northEastPanel.add(Box.createVerticalGlue());
    return northEastPanel;
  }

  private JPanel createSouthWestPanel()
  {
    JPanel southWestPanel = new JPanel();
    southWestPanel.setLayout(new GridLayout(3155));
    southWestPanel.add(new JButton("Widths"));
    southWestPanel.add(new JButton("Are"));
    southWestPanel.add(new JButton("Equal"));
    return southWestPanel;
  }

  private JPanel createSouthEastPanel()
  {
    JPanel southEastPanel = new JPanel();
    southEastPanel.setLayout(new FlowLayout());
    southEastPanel.add(new JButton("Centered?"));
    return southEastPanel;
  }

  public static void main(String[] argv)
  {
// For thread safety this should be utilized in the dispatch thread
    javax.swing.SwingUtilities.invokeLater(new Runnable()
    // Anonymous class
      public void run()
      {
        ConstraintExample example = new ConstraintExample();
      }
    });
  }
}

Try running the code and resize the frame. Observe how the components move as you resize, then experiment and change the code with alternate layout techniques to get a feel for how they work.

In the above example, a number of techniques are demonstrated. In the north west quadrant (top left), a label and a text area are placed within a row with a horizontal BoxLayout. This row is added to a JPanel with a BorderLayout. Only the NORTH region of this BorderLayout is utilized. This technique keeps the height of these two controls fixed, so that the text area does not resize vertically. The NORTH region of BorderLayout respects the preferred height of what it contains. An entire layout manager is being used here to constrain the height; all of the other regions of this BorderLayout are empty.

In the north east quadrant, two buttons are arranged with a BoxLayout manager. Notice the first and last component to be added here are calls to Box.createVerticalGlue(). Imagine that the glue spreads out, pushing anything it encounters away. In this case, the glue spreads out above the top component and below the bottom - centering the interior items vertically. A vertical strut is created between the two buttons to create some spacing.

In the south west quadrant, three buttons are created to have equal widths. This is a service provided by the GridLayout manager, and although the grid has only one column, each component within it shares the same width. Equal sizes are handily accomodated by the GridLayout manager.

In the south east quadrant, a single button is centered horizontally utilizing the FlowLayout manager. This manager only centers one component horizontally, its vertical position is a fixed offset. FlowLayout is best left in the drawer normally, but can be useful in a pinch.

A few parting tips

If you create a control, which is not part of the Swing component library, be sure to override the getPreferredSize() method so the layout managers can find out how large your component should be displayed. getMinimuSize() and getMaximumSize() should also be overridden, if your layout manager considers them.

Make sure that when the user resizes your frame it meaningfully expands areas. Text entry areas should grow horizontally to allow more space to type. Fixed sized labels do not need to expand. When the user requests a larger view by resizing, make their space more useful for them.

Finally, be sure to test your user interfaces with a variety of LookAndFeel packages, and also test with a broad array of possibly displayed values. Test on any target systems where your code will be used and verify it operates correctly. If certain configurations make it impossible to read your interface, you will have some surly users knocking on your door late at night.

Discuss this article in The Big Moose Saloon!


Return to Top
Meet the Author - Budi Kurniawan
Meet the Author
Budi Kurniawan
by Dirk Schreckmann

Budi Kurniawan is the author of the recently released, and self-published title, "How Tomcat Works." During the book promotion this week, Budi is hanging out with us, answering questions in our Apache/Tomcat forum. Participate and you might win a copy of his book!

JavaRanch: Budi, you joined the ranks of self-publishers with your recently released book, "How Tomcat Works." Why did you decide to endeavor to self-publish this time? What drove you away from the established publishers?

Budi: I wrote five computer books for various publishers before this one. I thought I could do better self publishing my books, because of the following reasons.

1. It's a widely known secret that authors get very little from their book sales, a merely 5 to 7% of the retail price. This means, an author only gets $2.50 or so for a $49.99 book. A computer book is considered successful (which means the publisher does not mind a second edition) if it sells at least 10,000 copies, and there are not many books this popular. That's why not many authors are willing to spend a lot of time to get their books to the highest quality possible. I spend a lot of time writing my books, thinking how to make what I write as easy to read and understand as possible. Self publishing should be able to give me more money that is more worth my time. In theory, I can make 5 times as much as going through a publisher. Therefore, my target for this book is only 2,000 copies.

2. Contracts with some publishers often put the authors in very unfavorable position. Have you ever heard that for any overseas sales, the author gets half the normal royalty rate even though the publisher makes more money (they give overseas buyers less discounts)? They claim they have invested a lot of money in the past to establish the overseas distribution channels. In reality, I found out later, trading with overseas bookstores is relatively easy. They even pay for the freight from New York to their home countries.

3. I want more control of what I write.

4. I have heard that most actors, in some stage of their career, want to be directors, that's the natural progression of their careers. I guess, many authors want to self publish too.

5. Many publishers don't spend money on advertising. I thought I could see my books in magazines, but I was wrong.

6. Self publishing is easy and relatively cheap. I mean, you just need $5000 and you can publish your book. It's very tempting to try.

7. Amazon.com accounts for 20% of book sales in the US, and I suspect it's even higher for computer books. Selling through Amazon is very easy too. So how I calculated it was like this. If my target is only 20% of what a publisher's (2,000 copies as opposed to 10,000), then I can just sell through amazon and get the same result, at the same time retaining the rights of the book.

8. I can sell direct. In the past I gave my reader a 45% discount, but then some bookstores were not happy, so now I'm selling at the full price.

9. You get 100% of the translation rights, and not 25% to 50%.

JavaRanch: What surprised you during this experience? What hadn't you expected?

Budi: I didn't know marketing was so difficult. Many bookstores don't even reply to my marketing emails or phone calls. I thought if the books were 100% returnable, they would be happy. No.

JavaRanch: What have you done to market your book? Have any particular approaches or activities been especially successful? What major retailers (on-line and brick-and-mortar) are selling your book?

Budi: To be honest, I'm still trying to find the best strategy. So far, through emails and sending samples to bookstores. I've opened an account with amazon and Baker & Taylor (a wholesaler) and sent sample copies to Barnes & Noble and Borders. Hopefully there will be some response. I also sell direct to SoftPro and other computer books in Australia, Germany, and the UK. The store in Sweden buys my book from Baker & Taylor. Reviews definitely help too. Another thing, I've contacted some other self-publishers, hoping to exchange notes on self publishing or maybe taking on the marketing part together. I don't mind telling other people what I've done. I feel the need for an association of computer book self publishers and small publishers.

JavaRanch: How many copies have you sold? When did the book hit the market?

Budi: It started selling early May this year, and I've sold about 400 copies to bookstores in 7 countries. The book has also been translated into 3 languages. I spent a lot of time making sure this book was easy to read and informative. I got rave reviews from all reviewers who had read this book, including from JavaRanch and Richard Monson-Haefel (the EJB guru and author of Enterprise JavaBeans 4th edition). All the three foreign publishers reviewed this book closely before deciding to pay an advance.

JavaRanch: Have you considered selling the book as an e-book?

Budi: Yes, I am considering ebooks as well. It will be available for sale from my website.

JavaRanch: How many different people with what different sets of skills did you work with to complete your book, "How Tomcat Works?"

Budi: There was a tech editor who later became my co-author, a graphic designer who did the layout and cover design, that's all. This is not too mention hundreds of people who downloaded the first draft and sent their helpful comments.

JavaRanch: What was the editing process that you used when writing your book, "How Tomcat Works?" How many editors did you work with? What types of feedback did they provide?

Budi: Initially Paul Deck was my technical editor. It was taking me so long (I had a day job) that I finally asked if he could contribute a few chapters, which he did. Basically, a technical editor checks the accuracy of the text and tests the applications. I was grateful for Paul's help. Every time I finished a chapter, I emailed it to him. He then added his comments in the text itself, which I then re-reviewed. Occasionally there was code that I had to correct, etc. With the current features of modern word processors, this is an easy process.

JavaRanch: Over how long of a period of time did you and your co-author spend how much time writing "How Tomcat Works" would you estimate?

Budi: This book took an extremely long time to finish because there was practically no documentation that detailed the internal workings of Tomcat. I had to read every single line of code and, at first, did not even know where to start. After spending six months or so, I started to get the big picture and soon I could start thinking about how I could explain the material in plain English. Tomcat is one of the most successful Java projects, and it's a huge and complex system, consisting of hundreds of classes and millions of lines of code. In all, it took me almost 2 years (part time) to finish. Compare this with my other books which take an average of 4 months.

JavaRanch: As a writer, did you find that self-publishing consumed too much of your time - time you'd have rather spent writing?

Budi: The fact that I have a good (Word) template helps a lot. I would say I spent 10% more time than if I had to write for another publisher because there was someone helping me with the layout. In truth, even if you are writing for a publisher, you have to do formatting yourself to some degree (i.e. make sure you use the correct style for text, figure captions, etc).

JavaRanch: Who did the page layout of the book? Did you have to master any new software packages? Did you work with an outside designer when coming up with the design of the book?

Budi: I paid someone to do page layout and cover design. In any publishing business, it goes more or less like this. The author writes in Word or WordPerfect (or Open Office, if you like) format, using the correct styles for each part of the text. After reviews by editors and the whole book is ready, the book is then laid out using PageMaker or QuarkXPress. It is not easy and takes a lot of time. The final result is a PDF file, ready to go to the printer.

JavaRanch: Aside from marketing costs, what expenses have you been faced with as a self-publisher? How much were these expenses?

Budi: First of all, there is the printing and editing cost. Then, there is fulfillment service and warehousing. Other than those, being a self-publisher, I don't incur much overheads, which means I don't have to publish a certain number of titles per year or at all. It does take a lot of my time though, especially for the first title. However, I believe, once I set up business relationships with customers the subsequent titles would be much easier to sell.

I spent $4,700 for printing 1,000 copies of How Tomcat Works. Other costs include paying the graphic designer, editor, for the ISBN registration, etc. The total cost is about $6,500, not counting the hundreds of hours spent on writing the book itself of course.

JavaRanch: Without divulging any secrets, could you describe the agreement you have with your printer? Are you working with a print-on-demand service? When researching printers, how many different outfits did you discuss this project with? Did you find that the costs varied quite a bit, from print shop to print shop?

Budi: There is no secret at all. Anyone can go to a printer's website to get a quote. As I mentioned earlier, it costs me $4,700 to print 1,000 copies. It would be cheaper (probably half the price) to print the next 1,000 though. I contacted four or five printers, and BoydPrinting in NY was the best I could get.

JavaRanch: Have you discovered any bottlenecks in your self-publishing process?

Budi: Yeah, this book needs much more exposure. Also, since I currently only have one title, big chains can simply ignore me. But I believe everything takes time and sales will be much better several months from now.

JavaRanch: Often, I've noticed that publishers offer one or two sample chapters from a book, downloadable from their web site. You're offering a full five chapters, plus the introduction. Why so many? Why did you decide that one or two chapters wouldn't be enough?

Budi: I realize this is a very advanced topics and many people may shy away from it before even giving the book a chance. Therefore, the five chapters are to prove to would-be buyers that the book is easy to follow, despite the heavy-weight discussions.

JavaRanch: Do you have any advice for would-be self publishers, thinking this might be an endeavor they'd like to embark on?

Budi: It's worth trying. I'm not 100% certain if I will be successful, but if you never try you'll never know. I believe I'm on the right track. Also, if you are thinking of self-publishing, do drop me an email. Maybe we can market our books together.

JavaRanch: Lastly, would you do it again?

Budi: I will publish at least 3 titles, and see what happens. The whole journey has been tiring but exciting. I'm currently writing a Struts design and programming title, which is much much easier to read and understand than all the Struts books in the market.


Budi Kurniawan is the author of "How Tomcat Works" (Brainysoftware.com, May 2004) and an IT consultant specializing in J2EE enterprise application development. In addition to a number of computer books, he has published about 100 articles for more than 10 publications--including prestigious Java magazines, such as java.net, JavaPro, JavaWorld, and Onjava.com. Budi is the author of the popular Brainysoftware.com File Upload Bean, which is licensed and purchased by many major corporations worldwide.

Discuss this article in The Big Moose Saloon!


Return to Top
Creating Multiple Language PDF using Apache FOP

Creating Multiple Language PDFs using Apache FOP

by Balaji Loganathan

This article will explain how to create PDFs in multiple languages using Apache FOP with XML and XSL. This article assumes that the reader is familiar with basic of Apache FOP, XML and XSL.

This is a 5 step process...

Step 1: Locate font.

For english you don't need to find extra fonts unless you need more style.
For other languages, you need to find either a TrueType or Type1 font.
For example for Arabic you can use the TrueType(Iqraa.ttf) font downloadable at http://www7.bev.net/civic/icb/ICB_Arabic.html
Store the specific font file in your hard disk say at C:\ folder.
Note: You have to explicitly tell to fo:block to use particular font for rendering other language data's, otherwise a ? or # symbol will appear in the generated PDF.

Step 2: Create a language resource XML file.


This XML file contains text values in various languages with an special element called "fontname" which will tell FOP what font to use for displaying the specific language text. For example Chinese text cannot be displayed using fonts like Helvetica or Arial, so we will assign specific font name for specific language.

Sample XML structure (Lets call it as Lang.xml)


<?xml version="1.0" encoding="utf-8" ?>
  <Lang>
     <en><!-- for english-->
      <fontname>Arial</fontname>
      <text1>Consignee!</text1>
     </en>
     <fr><!-- for french -->
      <fontname>Arial</fontname>
      <text1>Destinataire!</text1>
     </fr>
     <ar><!-- for Arabic -->
      <fontname>Naqsh</fontname>
      <text1>المرسل إليه</text1>
     </ar>
     <jp><!-- for Japanese -->
      <fontname>MSGothic</fontname>
      <text1>荷受人!</text1>
     </jp>
     <ch/> <!-- Chinese -->
  </Lang>

Step 3: Configure userconfig.xml

Now read the document at http://xml.apache.org/fop/fonts.html carefully, which will explain how to add, embed a new TrueType or Type1 font for FOP to understand the input character and display it at particular font style.
For example:
To import and use the arabic font C:\Iqraa.ttf, you have to generate the metrics file first using FOP TTFReader java file, like
>java org.apache.fop.fonts.apps.TTFReader C:\Iqraa.ttf Iqraa.xml
   
then you have to change your userconfig.xml file like
    <font metrics-file="Iqraa.xml" kerning="yes" embed-file="C:\myfonts\Iqraa.ttf">
      <font-triplet name="Iqraa" style="normal" weight="normal">
    </font>
this will tell FOP how to display text with Iqraa font style for Arabic texts.

Step 4: Configure the style sheet

Configure the XSL which you will use for converting the XML in to XSL:FO and then to PDF.
In the XSL file, try to import particular language data and store it in a XSL variable
For example the below code will store the fr\text1 value in the variable "message" and the fontname to use in the variable "font".
  <xsl:variable name="message" select="document('Lang.xml')/Lang/fr/text1"/>
  <xsl:variable name="font" select="document('Lang.xml')/Lang/fr/fontname"/>

Step 5: Use it

Now use this in fo:block like this
 <fo:block font-family="{$font}"><xsl:value-of select="$message"/></fo:block>
It is important to make sure that FOP.bat or FOP.sh is able to locate userconfig.xml, Iqraa.xml, Iqraa.ttf and LAng.xml
Make sure that you specify the option "-c userconfig.xml" while running the FOP
For example
>FOP -c userconfig.xml -xml InputXML.xml -xsl MultiLAng.xsl - pdf MultiLang.pdf

That's it.

With some XSL tricks you can make everything dynamic without hard coding any part.
For example: Arabic font always starts at right end which can be made dynamic by supplying some extra language specific tags in Lang.xml

The sample file MultiLang.xsl and Lang.xml can be used for your local testing, however it is important to configure the above mentioned steps for proper display of texts in PDF.You can also have a look at the generated PDF MultiLang.pdf

Now the question is UNICODE. Use XMLSPY or Visual Studio or equivalent editor to edit your Lang.xml file,
For example for to display "Consignee" in Chinese,  go to http://www.babylon.com/ copy and paste the Chinese word into the Lang.xml using the XML editor, the XML editors (like XMLSPY) will take care of encoding them to UTF8.

Discuss this article in The Big Moose Saloon!


Return to Top
The Trail Guide
by Paul Wheaton, trail boss

The three rules of using XML and Java:

Rule number one: Don't use XML. Lots of folks make their code "simpler" by replacing two lines of java with 40 lines of XML. Some apps get to the point where the developer needs to know java plus 18 different XML schemas.

Rule number two: If rule number one won't work, use XStream if possible. Mixing Java and XML cannot possibly get simpler than this.

Rule number three: If rule number one won't work and rule number two won't work, use JDOM.

Discuss this article in The Big Moose Saloon!


Return to Top
The Big Moose Saloon Question and Answer of the Month

Mosey on in and pull up a stool. The JavaRanch Big Moose Saloon is the place to get your Java questions answered. Our bartenders keep the peace, and folks are pretty friendly anyway, so don't be shy!

The Inquiry: An Informal Survey on the Use of Assert

Over in the Java in General (intermediate) forum, Dirk Schreckmann innocently inquired:

Are you using the assert keyword, introduced with Java 2 v1.4, when programming?

If not, why not?

Nineteen replies later, folks are still chimin' in with thoughts and experiences.

Now, mosey on o'er, see what folks are saying and chime in with some thoughts of your own.



Join this discussion in The Big Moose Saloon!


Return to Top
Book Review of the Month

JUnit Recipes: Practical Methods for Programmer Testing
J.B.Rainsberger, Scott Stirling
"Wow!" on two accounts: 1. I'm actually giving a 10 horseshoe rating to a book, and 2. "JUnit Recipes" is a very thorough and comprehensive encyclopedia of excellent advice and examples on almost every coding sitution I've ever wanted to test with JUnit.

J. B. Rainsberger has compiled a 700 page collection of scores of excellent recipes written in pattern-like fashion, clearly laying out testing problems in wont of solutions and the practical recipes for solving the problems, including annotated code examples, step-by-step instructions, and plenty of quality explanations.

"JUnit Recipes" is destined to be a classic, and has earned a most prominent place on my bookshelf, as I'm certain I'll be referencing it frequently for new and better ideas on formulating JUnit tests.

What's that? You'd like to borrow my copy of "JUnit Recipes?" No, get your own.

(Dirk Schreckmann - Sheriff, August 2004)
More info at Amazon.com || More info at Amazon.co.uk


Other books reviewed in August :

Java Cookbook: Solutions and Examples for Java Developers by by Ian Darwin
Just Java 2 by by Peter van der Linden
Tapestry In Action by Howard M. Lewis Ship
The Definitive Guide to SWT and JFace by Rob Warner, Robert Harris
Explorer's Guide to the Semantic Web by Thomas B. Passin
Pragmatic Project Automation by Mike Clark
Java 2, v5.0 (Tiger) New Features by Herbert Schildt
Better Faster Lighter Java by Bruce A. Tate, Justin Gehtland
J2ME Games with MIDP2 by Carol Hamer
Struts: Essential Skills by Steven Holzner
Mastering JavaServer Faces by Bill Dudney, Jonathan Lehr, Bill Willis, LeRoy Mattingly
Software Architecture Design Patterns in Java by Partha Kuchana
Java 1.5 Tiger : A Developer's Notebook by David Flanagan, Brett McLaughlin


Discuss this book review in The Big Moose Saloon!


Return to Top

Following are the scheduled promotions coming in September and October. Be sure to check our promotions page often for changes and new promotions. Participate in a promotion and you might win a copy of a book!

September 14 How Tomcat Works Budi Kurniawan, Paul Deck BrainySoftware.com Apache/Tomcat
September 21 Pragmatic Project Automation: How to Build, Deploy, and Monitor Java Apps Mike Clark The Pragmatic Programmers Ant, Maven and Other Build Tools
September 28 Just Java(TM) 2 (6th Edition) Peter van der Linden Pearson Education Java in General (beginner)
September 28 Eclipse: Building Commercial-Quality Plug-ins Eric Clayberg, Dan Rubel Addison-Wesley Professional IDE's and Other Tools
October 5 Java Testing and Design: From Unit Testing to Automated Web Tests Frank Cohen Pearson Education Testing
October 5 Fearless Change: Patterns for Introducing New Ideas Mary Lynn Manns, Linda Rising Addison-Wesley Pub Co Process
October 12 Better, Faster, Lighter Java Bruce A. Tate, Justin Gehtland O'Reilly EJB and Other J2EE Technologies
October 19 Java 1.5 Tiger : A Developer's Notebook Brett McLaughlin O'Reilly Java in General (intermediate)
October 26 Whizlabs SCDJWS Certification Exam Simulator   Whizlabs Web Services Certification (SCDJWS)
October 26 Spring Live Matt Raible SourceBeat.com Web Application Frameworks
October 26 Apache Axis Live James Goodwill SourceBeat.com Web Services



Return to Top
Managing Editor: Dirk Schreckmann