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 ImmutableSo 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?
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.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 PoolIf 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:
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:
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 CollectionWhat 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.
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. ConclusionLike 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:
API Spec for String JLS, §3.10.5 String Literals Until next time, Corey Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
Driving On CruiseControl - Part 1Lasse KoskelaAccenture 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:
"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. Setting up continuous integration with CruiseControlNext, 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:
...and, of course:
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: 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 ProjectSomething 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:
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 CopiesSince 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 CruiseControlCruiseControl 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: 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.
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 CruiseControlIn 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: 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: 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... ResourcesA .zip file of the sample project. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
Examining Layouts with Swing by Tom Tolman 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.
The The First Secret
The first secret to working with the standard Layout managers is to utilize 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 The example below uses nested
In the above example the first name label and first name text entry are both added into a The Second SecretThe second secret of using the standard layout managers is to exploit the constraints each provide you.
The example below uses some of these constraints to manipulate the layout. The main panel is a two by two
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
In the north east quadrant, two buttons are arranged with a
In the south west quadrant, three buttons are created to have equal widths. This is a service provided by the
In the south east quadrant, a single button is centered horizontally utilizing the A few parting tipsIf you create a control, which is not part of the Swing component library, be sure to override thegetPreferredSize() 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 Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
Creating Multiple Language PDFs using Apache FOPby 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.xmlthen 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. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 AssertOver in the Java in General (intermediate) forum, Dirk Schreckmann innocently inquired:
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 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
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!
Return to Top |