JETZT ONLINE BESTELLEN
First Edition Mai 2008
ISBN 978-0-596-52793-8
910 Seiten
EUR49.00
Weitere Informationen zu diesem Buch
Inhaltsverzeichnis | Kolophon |
Inhaltsverzeichnis
- Chapter 1: Setting Up a Project Using Ant
- InhaltsvorschauAnt is a popular and widely used open source build tool, written in Java. Indeed, it is probably the most widely used build tool in the Java world. It is supported by virtually all modern IDEs, and (being a Java application) runs on almost any Java-friendly .In a nutshell, Ant helps you transform the source code, libraries, and other files that make up your project into a deliverable software package. And, more importantly, it helps you do this in an orderly, repeatable manner. If you design your build script well, you can also ensure that it will behave the same way on any machine. This leads the way to automatic builds, which can be run on a remote machine with little or no human intervention. In , we discuss how to use Continuous Integration tools to take this process even further.Ant is a highly flexible tool—you can make it do pretty much anything you want. However, this flexibility can come at a cost of complexity. Good Ant build scripts are written in a way that will be easy to read and maintain in a year’s time. In this chapter, I will try to show some best practices that should help make your scripts clean and .At the time of this writing, there were no graphical installers available, although Ant is now provided as a standard package for many Unix distributions. Nevertheless, you will often get a more complete installation if you download and install Ant manually.Ant should run correctly on any Java-friendly platform. Ant is a pure Java tool, so it does require a (preferably recent) JDK installation to work.So, as a prerequisite for installation, you should check that your machine has a correctly installed JVM:
$ java -version java version "1.6.0" Java(TM) SE Runtime Environment (build 1.6.0-b105) Java HotSpot(TM) Server VM (build 1.6.0-b105, mixed mode)The following two sections will discuss how to install Ant on Unix and Windows .Download the latest stable binary release of Ant from the Ant web site, and extract it into an appropriate directory. On my machine, I installed it into theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Ant in the Build Process
- InhaltsvorschauAnt is a popular and widely used open source build tool, written in Java. Indeed, it is probably the most widely used build tool in the Java world. It is supported by virtually all modern IDEs, and (being a Java application) runs on almost any Java-friendly .In a nutshell, Ant helps you transform the source code, libraries, and other files that make up your project into a deliverable software package. And, more importantly, it helps you do this in an orderly, repeatable manner. If you design your build script well, you can also ensure that it will behave the same way on any machine. This leads the way to automatic builds, which can be run on a remote machine with little or no human intervention. In , we discuss how to use Continuous Integration tools to take this process even further.Ant is a highly flexible tool—you can make it do pretty much anything you want. However, this flexibility can come at a cost of complexity. Good Ant build scripts are written in a way that will be easy to read and maintain in a year’s time. In this chapter, I will try to show some best practices that should help make your scripts clean and .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Ant
- InhaltsvorschauAt the time of this writing, there were no graphical installers available, although Ant is now provided as a standard package for many Unix distributions. Nevertheless, you will often get a more complete installation if you download and install Ant manually.Ant should run correctly on any Java-friendly platform. Ant is a pure Java tool, so it does require a (preferably recent) JDK installation to work.So, as a prerequisite for installation, you should check that your machine has a correctly installed JVM:
$ java -version java version "1.6.0" Java(TM) SE Runtime Environment (build 1.6.0-b105) Java HotSpot(TM) Server VM (build 1.6.0-b105, mixed mode)The following two sections will discuss how to install Ant on Unix and Windows .Download the latest stable binary release of Ant from the Ant web site, and extract it into an appropriate directory. On my machine, I installed it into the/usr/sharedirectory. I also created a convenient symbolic link to make later updates easier:# cd /usr/share # tar xvfz apache-ant-1.7.0-bin.tar.gz ant-1.7.0 # ln -s ant-1.7.0 Ant
Now, you should have a full Ant distribution installed on your machine:# ls -al /usr/share/ant lrwxrwxrwx 1 root root 9 2007-08-04 22:36 Ant -> ant-1.7.0 # ls /usr/share/ant bin fetch.xml KEYS LICENSE.dom NOTICE docs get-m2.xml lib LICENSE.sax README etc INSTALL LICENSE LICENSE.xerces WHATSNEW
Once this is done, you need to set up some environment variables. Ant requires two to function. First, set the ANT_HOME variable to the directory where you have installed Ant. You also need to ensure that the JAVA_HOME variable is correctly defined, as the Ant startup script uses both of these variables. This usually goes in one of your environment initialization scripts (for example, if you are using Bash, you could place this configuration in the ~/.bashrc file if you just need to set it up for your account, or in if you want to set it up for all users on this machine):ANT_HOME=/usr/share/ant JAVA_HOME=/usr/lib/jvm/java PATH=$PATH:$ANT_HOME/bin:$JAVA_HOME/bin export PATH ANT_HOME JAVA_HOME
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - A Gentle Introduction to Ant
- InhaltsvorschauNow that you have installed Ant, we can take it through its paces. Ant is an extremely powerful tool, and experienced users can really make it sing. We will start off simply, going through how to set up a small Java project with Ant.Ant build files are written in XML, using a quite readable set of tags. The overall structure of an Ant file is relatively straightforward and intuitive. An Ant build file describes how to build one (and only one) project. A build project is made up of targets and tasks.A target is a goal that you want your build process to achieve. Such a goal might be to compile your application code, run your tests, or prepare a production-ready release package. You can (and usually do) define several targets in your build file, which you may run on different occasions.A target can also depend on other targets. This lets you ensure that certain targets are always executed before others. For example, you might want to ensure that your unit test’s target is always run before you prepare a new release. You do this by declaring dependencies between your targets.A task lets you define a piece of work you want done. This might be compiling some Java source code using javac, running a set of unit tests using JUnit, or generating a production-ready JAR file using the jar utility. This is where the work actually gets done. The task is the workhorse of the Ant build process and the source of a lot of its power and flexibility. Behind the scenes, Ant tasks are actually implemented as Java classes, which makes it fairly easy to extend Ant by writing new tasks. This is one of the reasons that the majority of Java tools, both open source and commercial, provide an Ant task library. Indeed, Ant comes with a rich library of built-in (or “core”) and optional tasks. Core tasks are built into Ant, and need no special configuration. Optional tasks are maintained by the Ant team and delivered with Ant, but they rely on an external library. For example, theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Compiling Your Java Code in Ant
- InhaltsvorschauIn Java development, one of the most fundamental things that any build script needs to do is compile your code. In Ant, the <javac> task provides a convenient one-stop shop for Java compilation.Let’s look at a simple use of the <javac> task. In the example given above, we use a simple form of this task to compile the Java classes in the
src/maindirectory, and place the compiled classes in the build/classes directory:<target name="compile" depends="init" description="Compile Java code"> <javac srcdir="src" destdir="build/classes"/> </target>This is equivalent to executing the javac tool as shown here:$ javac -nowarn -d build/classes src/mainAdmittedly, you would rarely compile an application this simple in the real world. In most real applications, your application will require a set of libraries both to compile and to run. In Ant projects, these libraries are often stored (in the form of JAR files) in a directory calledlib. When you compile your application, you need to tell Ant where to find these libraries.Ant comes with some very powerful features to help you do this. Ant excels at defining and manipulating classpaths and other file path definitions. In Ant, these definitions are known as “path-like” structures, and they are used extensively in all but the most trivial build files. At its simplest, you can use the <path> tag to identify a particular library using the location attribute:<path id="junit.classpath" location="lib/junit.jar"/>
This path could also be defined in the same way using the path attribute. However, in addition to defining single JAR files or directories, the path attribute lets you use a more path-like construction using a combination of directory paths and a JAR file, as shown here:<path id="junit.classpath" path="build/classes:lib/junit.jar"/>
One of the nice things about the Ant path-handling features is that they are (like the underlying Java APIs) portable across different operating systems. Here, by convention, we use Unix-style paths containing forward slashes (“/”). For example, on a Windows machine, Ant will automatically translate the above path into “build\classes;lib\.”Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Your Build Script Using Properties
- InhaltsvorschauIn development, people often speak of the DRY (Don’t Repeat Yourself) principle. This is a general principle aimed at making code more readable and easier to maintain by avoiding the duplication of data. In a Java application, for example, key values that are used in several places, or that may need to be modified easily, are stored as constants or as parameters in a configuration file. This makes the code easier to read and understand, and makes maintenance less of a headache.In Ant, you can do the same sort of thing by using the <property> tag, which lets you declare constant values that can be used throughout the rest of your build file. Declaring a property is straightforward; you just need to provide a name and value attribute, as shown here:
<property name="javac.debug" value="on"/>
If you are referring to a directory, you can use the location attribute instead of value:<property name="build.dir" location="build"/>
This property value will be set to the absolute filename of thebuilddirectory.To use a property elsewhere in the build file, you just quote the name of the property surrounded by ${...}. For example, you can subsequently refer to this property anywhere in your build file as ${build.dir}. You could use the <echo> task to display the property value to the screen, as shown here:<target name="display-properties"> <echo>Debug = ${javac.debug}</echo> <echo>Build directory (build.dir) = ${build.dir}</echo> </target>Calling this target would display the value of this property on the console. On my machine, it displays the following:$ ant display-properties Buildfile: build.xml display-properties: [echo] Build directory (build.dir) = /home/john/projects/tax-calculator/build BUILD SUCCESSFUL Total time: 0 secondsAnother way to do this would be to use the <echoproperties> task, which, as the name suggests, lets you display all of the current property values to the console:<target name="display-properties"> <echoproperties /> </target>Properties can also be used to take into account environment differences on each developer’s machine. For example, you may need to store a path to a local server or to application directories, which may be different on different machines. To do this, you can store local properties in an ordinary Java properties file. The following directories would be different on Unix and Windows machines. On a Linux development box, you might have the following set of properties:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running Unit Tests in Ant
- InhaltsvorschauUnit testing is a fundamental part of Java development, and, applied correctly, can contribute substantially to the quality and reliability of your code. Modern coding techniques, such as test-driven development (TDD) and, more recently, behavior- development, rely heavily on unit testing to ensure that code is both well designed and well tested.Unit testing is good, but automated unit testing is better. When your unit tests are integrated into your build process, you can run them automatically before each build or before committing code to your version control system. This helps to ensure that your latest changes haven’t introduced any new errors. This is also known as regression testing. It makes refactoring your code safer and easier, as you can change code without having to worry about unknowingly breaking existing code—as long as you rerun the entire set of unit tests at frequent intervals.Let’s look at how to integrate JUnit tests into your Ant build process.JUnit (see ) is probably the most well-known and widely used unit testing framework in the Java world. A groundbreaking piece of software in its time, JUnit 3 is still the basis of a very large range of unit testing libraries. The more recent JUnit 4 introduces annotation-based testing and some other more modern features.You can run your JUnit tests in Ant using the <junit> task, which is what’s known as an optional task. In Ant, optional tasks are tasks that generally depend on external libraries, such as the JUnit library in this case. Ant comes bundled with the task itself, but, if you want to use the task, you need to provide the external library yourself. This is as opposed to core tasks, which are fully integrated into Ant and need no external libraries.Historically, Ant’s JUnit integration has been a bit rough. In older versions of Ant (prior to Ant 1.7), you need to place your own copy of the
junit.jarfile into the$ANT_HOME/libdirectory, alongside theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Generating Documentation with Javadoc
- InhaltsvorschauTechnical documentation is an important part of any project, and Javadoc is one of the cornerstones of technical documentation in Java. Javadoc produces a quite decent, usable set of API documentation for your Java code, which can be a valuable aid as a communication tool, helping team members understand what other team members are doing. Of course, good Javadoc requires well-written and meaningful comments within the source code, and enforcing that is a tall order for any tool. And Javadoc documentation is by definition low-level reference material—it can be very useful for an application developer familiar with the application, but it will be of limited use for a new developer trying to learn the application architecture. Despite these reservations, Javadoc should be an integral part of any development project.It is a good idea to generate Javadoc as part of your build process. Javadoc documentation should be generated and published alongside the compiled source code and the unit test results as part of the automatic build lifecycle. In Ant, you can do this using the <javadoc> task. This task has a lot of attributes and nested elements, but only a few are essential. Here is a simple example:
<target name="javadoc" depends="compile,init" description="Generate JavaDocs."> <javadoc sourcepath="${src.dir}" destdir="${reports.javadoc}" author="true" version="true" use="true" access="private" linksource="true" windowtitle="${ant.project.name} API"> <classpath> <path refid="compile.classpath" /> <pathelement path="${build.classes.dir}" /> </classpath> <doctitle><![CDATA[<h1>${ant.project.name}</h1>]]></doctitle> <bottom><![CDATA[<i>Copyright © 2007 All Rights Reserved. </i>]]></bottom> </javadoc> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Packaging Your Application
- InhaltsvorschauOnce you have compiled and tested your application, the next step is to bundle up the compiled code into a deliverable application or library. This can take different forms, depending on your project: you may have to prepare a JAR file, a WAR file, or possibly a ZIP or TAR file containing the executable code plus other files such as documentation and source code. Ant has many powerful features that can help you prepare your application for delivery. In the following sections, we will look at a few of the more interesting ones.The most fundamental Java packaging mechanism is the JAR file. A JAR file is essentially a ZIP file containing a hierarchy of compiled Java classes, plus some metadata. WAR and EAR files are similar, with some extra constraints on their internal directory structure and content.The basic usage of the <jar> task is simple. Here is an example from our sample application, where we bundle the compiled classes into a JAR file:
<property name="project.name" value="{ant.project.name}" /> <property name="project.version" value="1.0" /> ... <target name="package" depends="compile" description="Generate JAR file"> <jar destfile="${dist.dir}/${project.name}-${project.version}.jar" basedir= "${build.classes.dir}"/> </target>Running this will (surprise, surprise!) generate a JAR file containing your compiled classes:$ ant clean package Buildfile: build.xml clean: [delete] Deleting directory /home/wakaleo/projects/jpt-sample-code/ant-demo/dist [delete] Deleting directory /home/wakaleo/projects/jpt-sample-code/ant-demo/reports init: [mkdir] Created dir: /home/wakaleo/projects/jpt-sample-code/ant-demo/dist [mkdir] Created dir: /home/wakaleo/projects/jpt-sample-code/ant-demo/reports/xml [mkdir] Created dir: /home/wakaleo/projects/jpt-sample-code/ant-demo/reports/html compile: package: [jar] Building jar: /home/wakaleo/projects/jpt-sample-code/ant-demo/dist/ tax-calculator-1.0.jar BUILD SUCCESSFUL Total time: 0 secondsWe use the Maven convention for naming JAR files here, which is to add the version number to the end of the filename. This makes it easier to identify file versions at a glance. The project name comes from theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Deploying Your Application
- InhaltsvorschauOnce you have generated a packaged version of your application, you will certainly want to deploy it. For example, if you are developing a web application, you may want to deploy it to a web server on your own machine, or to a remote server for testing. Or, if you are developing a shared library, you may copy your latest version to a local web server, where other users can consult the documentation and download the API.The simplest way to deploy an application is to copy the packaged file to the target server. Of course, this will only work if the target server is hosted on the development machine or if the target server has a shared drive that can be mapped to from the development/build machine, and the current user has write access to these directories. Because this is generally the case for a local development machine, this approach is often a simple, pragmatic way to deploy (and redeploy) a web application to a locally running application server. You can copy a file to another directory by using the <copy> task, as shown here:
<property name="tomcat.install.dir" location="${user.home}/servers/tomcat /apache-tomcat-5.5.23" /> <target name="local.deploy" depends="war" description="Deploy to local Tomcat instance"> <copy file="${dist.dir}/${project.name}-${project.version}.war" todir="${tomcat.install.dir}/webapps" /> </target>In this example, we simply defined a property pointing to a local Tomcat installation, and used the <copy> task to copy the generated WAR file to the Tomcat webapps directory, where Tomcat will be able to pick it up and deploy it automatically. Many application servers work in the same way.Of course, you may want to rename the WAR file on the way. Typically, you may want to strip off the version number when you deploy the web application so that users can simply access the application using the project name. You can do this using the tofile attribute instead of todir:<property name="tomcat.install.dir" location="${user.home}/servers/tomcat /apache-tomcat-5.5.23" /> <target name="local.deploy" depends="war" description="Deploy to local Tomcat instance"> <copy file="${dist.dir}/${project.name}-${project.version}.war" tofile="${tomcat.install.dir}/webapps/${project.name}.war" /> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Bootstrapping Your Build Scripts
- InhaltsvorschauAs a rule, your build scripts should be as portable as possible. In an ideal world, a new user with a vanilla installation of Ant should be able to check out the project source code and use the build scripts immediately, as-is. No extra configuration should be required—no deploying JAR files in strange places, no setting up exotic configuration files, and so on.Of course, in real-world projects, things often aren’t quite this simple. Your build file will often use nonstandard tasks that need to be installed. It is a tiresome task to hunt down and install the dozen or more Ant extensions that a real-world Ant build file will typically need, and issues may arise if different users have different versions of the extension libraries. This is clearly a good place to automate things.One useful technique for doing this is writing bootstrap build files that download and install extra libraries that your project needs. This is fairly easy to do using standard Ant tasks such as <get>, <unzip>, and <available>. An example of a typical bootstrap script (called
bootstrap-findbugs.xml), which downloads and installs the Findbugs package, is shown here:<project name="FindBugs Bootstrap script" default="bootstrap" basedir="." > <!-- Define the environment-specific variable "findbugs.home" in this file. --> <property file="${user.home}/ant-global.properties"/> <!-- This default values used if no properties file is present --> <property name="findbugs.home" value="${user.home}/.findbugs"/> <property name="findbugs.version" value="1.2.0"/> <echo>Installing FindBugs into ${findbugs.home}</echo> <property name="sourceforge.mirror" value="http://optusnet.dl.sourceforge.net/sourceforge" /> <available file="${findbugs.home}/findbugs.zip" property="findbugs.installed"/> <echo>Bootstrap FindBugs</echo> <target name="bootstrap" unless="findbugs.installed"> <echo>Installing FindBugs</echo> <mkdir dir="${findbugs.home}" /> <get src="${sourceforge.mirror}/findbugs/findbugs-${findbugs.version}.zip" dest="${findbugs.home}/findbugs.zip" usetimestamp="true"/> <unzip src="${findbugs.home}/findbugs.zip" dest="${findbugs.home}"/> <move todir="${findbugs.home}"> <fileset dir="${findbugs.home}/findbugs-${findbugs.version}"> <include name="**/*"/> </fileset> </move> <delete dir="${findbugs.home}/findbugs-${findbugs.version}"/> </target> </project>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Maven Dependencies in Ant with the Maven Tasks
- InhaltsvorschauOne of the key features of Maven (see ) is its use of a central repository to store dependencies and identify the libraries needed by an application. Maven 2 also supports transitive dependencies, a powerful concept that lets you limit the dependencies you need to declare to the strict minimum.When you bundle and deploy an application, you not only need to include the libraries that your application requires, but you also need to include the additional libraries required by these libraries to work. So, if your application uses Hibernate, you will also need all of the libraries required by Hibernate. In real-world projects, this can amount to quite a list.If your build framework supports transitive dependency management, you need only to declare the libraries that your application uses directly. The build tool will take care of the others. So, if your application uses Hibernate, you only need to state the exact version of Hibernate you are using, and not the other libraries that Hibernate needs. This makes your project dependencies simpler and easier to understand.Ant does not support dependency management “out-of-the-box.” In Ant projects, all the libraries needed by an application are typically placed in a project directory—sometimes stored in the version control system, sometimes not (opinions vary on this point). This can create a number of problems because it is hard to know exactly which versions of libraries are required or currently being used. When libraries are stored in the version control system, projects can take up a lot of space (particularly if CVS is used), and they can take a long time to download. By contrast, if they are not stored in the version control system, some convention needs to be established to work out where to obtain the libraries needed for a given project.A much better approach is to use one of several Ant extensions to manage dependencies declaratively. In this chapter, we will discuss the Maven 2.0 Ant tasks.An alternative approach to dependency management in Ant is to use Ivy. Ivy is a powerful and flexible dependency management tool that integrates well into Ant projects, and it provides some interesting features as well as some nice reporting capabilities (). Ivy can also leverage the rich public Maven repositories such as Ibiblio and Codehaus. Although we don’t have space to look at Ivy in this book, it may be worth a look if you are evaluating dependency management tools for Ant.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Using Ant in Eclipse
- InhaltsvorschauAnt is well-supported in virtually all modern Java IDEs, and Eclipse is no exception. Eclipse allows you to create a new Eclipse project using an existing Ant file, and recognizes the structure of Ant build files. The Outline view gives you a structured vision of your build file. In addition, you can execute any target directly from within Eclipse using the contextual menu (see ).
Figure : Using Ant in EclipseEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Ant in NetBeans
- InhaltsvorschauAnt integrates smoothly into NetBeans. Indeed, by default, NetBeans uses Ant internally to organize your project, even if you don’t ask it to. NetBeans automatically recognizes Ant build files and displays the build file targets. As in Eclipse, you can execute targets directly using the contextual menu (see ).
Figure : Using Ant in NetBeansEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Manipulating XML with XMLTask
- InhaltsvorschauContributed by: Brian AgnewFor simple text search and replace operations, the Ant <replace> task is sufficient. But in modern Java frameworks, you are more likely to need powerful XML manipulation capabilities to modify servlet descriptors, Spring configurations, and the like.XMLTask is an Ant external task that provides powerful XML editing tools focused on creating and changing XML files as part of a build/deployment process.What are the advantages of using XMLTask?
- Unlike the Ant task, <replace> XMLTask gives you the ability to identify parts of an XML document using XPath, and to insert, remove and copy XML at these locations. You can use XPath to simply identify an XML element, or use more complex logic with predicates (“find me the element called ‘X’ with an attribute of ‘Y’…”).
- XMLTask is “XML-aware.” This means that you can’t create an XML document that isn’t well-formed. XMLTask will handle character-encoding issues, whereas <replace> has no knowledge of the encoding requirements of your XML . For example, <replace> will allow you to insert the characters “<,” “>,” and “&” into an XML document without using the corresponding entities (“<” “>” and “&”), and thus possibly break the “well-formedness” of your .
- XMLTask doesn’t require you to learn or use XSLT to perform XML manipulations. It uses intuitive instructions such as insert, replace, and remove.
XMLTask is easy to use. Take a look at the XMLTask home page, or download from Sourceforge. You don’t need to be knowledgeable about XPath to use XMLTask, but if you need an introduction, take a look at the tutorial on http://www.zvon.org.Let’s look at a simple example. Imagine you have a Spring configuration that you want to modify. For instance, you may be making changes for development, test, and release versions, and want to perform insertions, replacements, and removals.A simple XMLTask is shown below:<project name="xmltask-demo" default="main"> <!--xmltask.jar should be referenced via lib, or in the ${ant.home}/lib or similar --> <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/> <!-- you may need to reference a local copy of the DTD here if your XML documents specify one. See below for more info --> <xmlcatalog id="dtd"> <dtd publicId="-//SPRING//DTD BEAN//EN" location="./spring-1.0.dtd"/> </xmlcatalog> <target name="main"> <xmltask source="spring-template.xml" dest="spring.xml" preserveType="true"> <xmlcatalog refid="dtd"/> <insert path="/beans" position="under"> <![CDATA[ <bean id="bean-to-insert" class="com.oopsconsultancy.example.Bean1"> <constructor-arg index="0"> ..... </constructor-arg> </bean> ]]> </insert> </xmltask> </target> </project>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauUsing XMLTask, you can maintain, create, and modify XML files with a tool that is much more powerful than the standard <replace> or file creation tasks, and yet not have to worry about using XSLT. We’ve not covered many of its functions here. See the home page () for more information and examples. A mailing list (subscription only) is also available ().Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 2: Setting Up a Project Using Maven 2
- InhaltsvorschauIn this chapter, we look at the second major player in the Java build tools arena: . Maven is an increasingly popular open source build management tool for enterprise Java projects, designed to take much of the hard work out of the build process. Maven uses a declarative approach, in which the project structure and contents are described, rather then the task-based approach used in Ant or in traditional Make files or shell scripts. Maven also strongly promotes the use of standard directory structures and a well-defined build lifecycle. This helps enforce company-wide development standards and reduces the time needed to write and maintain build scripts.Maven’s authors describe Maven as a “project management framework,” and it is indeed much more than just a simple build scripting tool. Maven’s declarative, standards-based approach to project build management simplifies many aspects of the project lifecycle. As well as catering for compiling, building, testing, and deploying your with a minimum of effort, Maven offers a number of other key advantages:
- Project dependencies are declared and managed in a clean, transparent way, which reduces the risk of dependency-related errors and makes for better documentation.
- Maven lets you easily generate useful, high-quality, technical documentation and reports about the current state of the project and project team members. Note that we aren’t taking about a good user manual, which is an altogether different issue, but, rather, about technical documentation, written by developers for developers. In many technical projects, decent technical documentation is woefully inadequate. It is nevertheless a vital part of modern software development, especially when dislocated teams are involved.
- Maven proposes a clear standard directory layout for source code, project resources and configuration files, generated output, and project documentation. This makes it easier to understand new Maven projects, and also makes the Maven build scripts cleaner and simpler.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Maven and the Development Build Process
- InhaltsvorschauIn this chapter, we look at the second major player in the Java build tools arena: . Maven is an increasingly popular open source build management tool for enterprise Java projects, designed to take much of the hard work out of the build process. Maven uses a declarative approach, in which the project structure and contents are described, rather then the task-based approach used in Ant or in traditional Make files or shell scripts. Maven also strongly promotes the use of standard directory structures and a well-defined build lifecycle. This helps enforce company-wide development standards and reduces the time needed to write and maintain build scripts.Maven’s authors describe Maven as a “project management framework,” and it is indeed much more than just a simple build scripting tool. Maven’s declarative, standards-based approach to project build management simplifies many aspects of the project lifecycle. As well as catering for compiling, building, testing, and deploying your with a minimum of effort, Maven offers a number of other key advantages:
- Project dependencies are declared and managed in a clean, transparent way, which reduces the risk of dependency-related errors and makes for better documentation.
- Maven lets you easily generate useful, high-quality, technical documentation and reports about the current state of the project and project team members. Note that we aren’t taking about a good user manual, which is an altogether different issue, but, rather, about technical documentation, written by developers for developers. In many technical projects, decent technical documentation is woefully inadequate. It is nevertheless a vital part of modern software development, especially when dislocated teams are involved.
- Maven proposes a clear standard directory layout for source code, project resources and configuration files, generated output, and project documentation. This makes it easier to understand new Maven projects, and also makes the Maven build scripts cleaner and simpler.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Maven and Ant
- InhaltsvorschauWithout a doubt, the most popular and most well-known build tool in the Java sphere is Ant. Ant (see ) is a fine tool and a hugely successful open source project. Millions of Java developers are familiar with it. And, as we will see throughout the rest of the book, there is hardly a Java tool in existence that doesn’t integrate with Ant.However, when you write a lot of Ant build scripts, you find yourself asking yourself (and other teamg members) the same questions over and over again: Where will the source code go? What about the unit tests? How do we handle dependencies? How will we bundle up the deliverable application? What shall we call the main targets? Individually, Ant lets you deal with each of these tasks with a high degree of flexibility and power. However, you still have to write the tasks from scratch or duplicate and modify an Ant script from a previous project. And when you move to a new project or company, you need to ask these questions once again to (begin to) understand the build process in place.Many (although not all) projects do follow fairly common and well-known patterns. A lot of what you need to configure in your build process is pretty much run-of-the-mill. It always seems a shame to redo the work again for each new project.Maven can help you here. Maven takes a lot of the grunt work out of the build process, and tries to lever the combined experience and best practice of a large community of developers. By adhering to a certain number of conventions and best practices, Maven lets you remove the drudgery of all the low-level tasks in your build scripts. In the rest of this chapter, we will see how.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Maven
- InhaltsvorschauIn this chapter, we will go through how to install Maven 2 on various platforms. The basic installation process is straightforward, and is the same for all platforms. Maven is a pure Java tool, so first of all you need to ensure that there is a recent version of Java (1.4 or later) on your machine. Then, download the latest distribution from the Maven download site and extract it into an appropriate directory. Finally, just add the bin subdirectory to the system path.If you are familiar with installing Java tools, this should be enough to get you started. In the rest of this chapter, we discuss some more detailed environment-specific .In this chapter, we run through how to install Maven into a Unix environment.Installing Maven in a Unix-based environment is a relatively simple task. Download the latest version in the format of your choice, and extract it to an appropriate directory. Conventions vary greatly from one system to another, and from one system administrator to another: I generally place the maven installation in a nonuser-specific directory such as /usr/local, as shown here:
# cd /usr/local # tar xvfz maven-2.0.7-bin.tar.gz # ls
This will extract the maven installation in a directory calledmaven-2.0.7. For convenience, on a Unix system, I generally create a symbolic link to this directory to make upgrades easier to manage:# ln -s maven-2.0.7 maven # ls -al total 16 drwxr-xr-x 3 root root 4096 2006-08-06 13:18 . drwxr-xr-x 53 root root 4096 2006-07-20 21:32 .. lrwxrwxrwx 1 root root 11 2006-08-06 13:17 maven -> maven-2.0.7 drwxr-xr-x 6 root root 4096 2006-08-06 13:17 maven-2.0.7
Now just add themaven/bindirectory to your environment path. Typically, you will set this up in one of your environment initialization scripts (for example, if you are using Bash, you could place this configuration in the~/.bashrcfile if you just need to set it up for your account, or in /etc/bashrc if you want to set it up for all users on this machine). Don’t forget to make sure that theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Declarative Builds and the Maven Project Object Model
- InhaltsvorschauBefore we look at how to create and work with projects in Maven, we need to discuss some of the basics. The most fundamental of these is the Maven Project Object Model, or POM, which we will look at in this chapter. In the process, we also will cover some important basic principles of Maven development, as well as a lot of the key features of Maven. As many, if not most, new Maven users are already familiar with Ant, we will look at how the Maven approach differs from the one used by Ant, and how this can help simplify your builds.For Ant users, the Maven philosophy can take a little getting use to. Unlike Ant, which is very much task-oriented, Maven uses a highly declarative approach to project builds. In Ant, for example, you list the tasks that must be performed to compile, test, and deliver your product. In Maven, by contrast, you describe your project and your build process, relying on conventions and sensible default values to do much of the grunt work. The heart of a Maven 2 project, the POM, describes your project, its structure, and its dependencies. It contains a detailed description of your project, including information about versioning and configuration management, dependencies, application and testing resources, team members and structure, and much more. The POM takes the form of an XML file (called
pom.xmlby default), which is placed in your project home directory.Let’s look at a practical example. One of the most fundamental parts of any Java build process involves compiling your Java classes. In a typical Ant build, you would use the <javac> task (see ) to compile your classes. This involves defining the directory or directories containing your Java source code, the directory into which the compiled classes will be placed, and creating a classpath that contains any dependencies needed to compile your classes. Before invoking the compiler, you need to be sure to create the target directory. The corresponding Ant script might look something like this:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Understanding the Maven 2 Lifecycle
- InhaltsvorschauProject lifecycles are central to Maven 2. Most developers are familiar with the notion of build phases such as compile, test, and deploy. Ant build scripts typically have targets with names like these. In Maven 2, this notion is standardized into a set of well-known and well-defined lifecycle phases (see ). Instead of invoking tasks or targets, the Maven 2 developer invokes a lifecycle phase. For example, to compile the application source code, you invoke the “compile” lifecycle phase:
$ mvn compileSome of the more useful Maven 2 lifecycle phases are the following (see ):
Figure : Maven 2 lifecycle phases- generate-sources
- Generates any extra source code needed for the application, which is generally accomplished using the appropriate plug-ins.
- compile
- Compiles the project source code.
- test-compile
- Compiles the project unit tests.
- test
- Runs the unit tests (typically using JUnit) in the
src/testdirectory. If any tests fail, the build will stop. In all cases, Maven generates a set of test reports in text and XML test reports in thetarget/surefire-reportsdirectory (see ). - package
- Packages the compiled code in its distributable format (JAR, WAR, etc.).
- integration-test
- Processes and deploys the package if necessary into an environment in which integration tests can be run.
- install
- Installs the package into the local repository for use as a dependency in other projects on your local machine.
- deploy
- In an integration or release environment, this copies the final package to the remote repository for sharing with other developers and projects.
The full list is much longer than this, and can be found on the Maven web site.These phases illustrate the benefits of the recommended practices encouraged by 2: once a developer is familiar with the main Maven lifecycle phases, he or she should feel at ease with the lifecycle phases of any Maven project. The lifecycle phase invokes the plug-ins it needs to do the job. Invoking a lifecycle phase automatically invokes any previous lifecycle phases as well. Because the lifecycle phases are limited in number, easy to understand, and well organized, becoming familiar with the lifecycle of a new Maven 2 project is easy.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Maven Directory Structure
- InhaltsvorschauMuch of Maven’s power comes from the standard practices that it encourages. A developer who has previously worked on a Maven project immediately will feel familiar with the structure and organization of a new one. Time need not be wasted reinventing directory structures, conventions, and customized Ant build scripts for each project. Although you can override any particular directory location for your own specific ends, you really should respect the standard Maven 2 directory structure as much as possible, for several reasons:
- It makes your POM file smaller and simpler.
- It makes the project easier to understand and makes life easier for the poor guy who must maintain the project when you leave.
- It makes it easier to integrate plug-ins.
The standard Maven 2 directory structure is illustrated in .The POM (
Figure : A typical Maven directory structurepom.xml) and two subdirectories go into the project home directory: src for all source code and target for generated artifacts. The src directory has a number of subdirectories, each of which has a clearly defined purpose:src/main/java- Your Java source code goes here (strangely enough!)
src/main/resources- Other resources your application needs
src/main/filters- Resource filters, in the form of properties files, which may be used to define variables only known at runtime
src/main/config- Configuration files
src/main/webapp- The web application directory for a WAR project
src/test/java- Source code for unit tests, by convention in a directory structure mirroring the one in your main source code directory
src/test/resources- Resources to be used for unit tests, but that will not be
src/test/filters- Resources filters to be used for unit tests, but that will not be deployed
src/site- Files used to generate the Maven project web site
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring Maven to Your Environment
- InhaltsvorschauOne of the principal aims of Maven is to produce portable project build environments. Nevertheless, each work environment has its particularities, which need to be catered for. In this chapter, we investigate some common areas where you may need to tailor Maven to suit your particular work environment, such as configuring proxy servers, defining enterprise repositories, or specifying usernames and passwords.When it comes to defining environment-specific configuration details, the most important tool at your disposal is the
settings.xmlfile. Each user can have his or her own individualsettings.xmlfile, which should be placed in the$HOME/.m2directory. This file is not placed under version control, and therefore can contain details such as usernames and passwords, which should not be shared in the source code repository.If you are working in a company, you may well be accessing the Internet via a proxy. Maven relies heavily on accessing the Internet to download the libraries that it needs for your projects and for its own purposes. Therefore, if you are behind a proxy, you will need to tell Maven about it. Maven stores environment-specific parameters in a file called$HOME/.m2/settings.xml. You will have to create this file if it doesn’t already exist. To define a proxy, just add a <proxy> element in this file, as follows:<settings> <proxies> <proxy> <active>true</active> <protocol>http</protocol> <host>proxy.mycompany.com</host> <port>8080</port> <username>user</username> <password>password</password> <nonProxyHosts>*.mycompany.com</nonProxyHosts> </proxy> </proxies> </settings>The <nonProxyHosts> element is useful to define servers that do not need proxy access, such as internal enterprise repositories.Another common use of thesettings.xmlfile is to configure mirror servers. This typically is done to configure an organization-wide repository. Many organizations use a local repository to store and share internal packages and to act as a proxy to external repositories. This solution is faster and more reliable than requiring users to go to the Internet whenever a new dependency is required.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Dependency Management in Maven 2
- InhaltsvorschauDependency management is one of the more powerful features of Maven 2. Dependencies are the libraries you need to compile, test, and run your application. In tools such as Ant, these libraries typically are stored in a special directory (often called
lib), and are maintained either by hand or as project artifacts that are stored in the source code repository along with the source code. Maven, by contrast, uses a declarative approach. In a Maven project, you list the libraries your application needs, including the exact version number of each library. Using this information, Maven will do its best to find, retrieve, and assemble the libraries it needs during the different stages in the build lifecycle. In addition, using a powerful feature called Transitive Dependencies (see ,” later in this section), it will include not only the libraries that you declare but also all the extra libraries that your declared libraries need to work correctly.In this chapter, we will look at different aspects of how to handle dependencies in Maven 2.One of the most powerful features of Maven 2 is its ability to handle dependencies in a consistent and reliable manner. In the <dependencies> section of the POM file, you declare the libraries that you need to compile, test, and run your application. Dependencies are retrieved from local or remote repositories, and cached locally on your development machine, in the$HOME/.m2/repositorydirectory structure. If you use the same jar in two projects, it will only be downloaded (and stored) once, which saves time and disk space.In Maven, dependencies are handled declaratively. Suppose that your project needs to use Hibernate, and that your unit tests are written in JUnit. In this case, the dependency section in your POM file might look something like the following:... <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> ...Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Looking for Dependencies with MvnRepository
- InhaltsvorschauWhen you are working with Maven, you often need to look up a particular dependency so that you can add it to your POM file. It can be quite tricky to remember and/or hunt down the precise group and artifact names and the latest version numbers for any but the most well-known artifacts. For example, do you remember the exact group and latest version of the Hibernate or Spring MVC libraries?One useful resource that can help out here is the MvnRepository site (see ). Using this site, you can search the central Maven repository for artifacts by name. When you find the version you are looking for, simply copy the displayed dependency block into your POM file. While you’re there, you also can list the dependencies of a particular library, view the latest updates to the repository, or browse the overall structure of the repository.
Figure : The MVN Repository web siteEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Project Inheritance and Aggregation
- InhaltsvorschauMaven actively encourages you to write your projects as a set of small, flexible, modules rather than as a monolithic block of code. Dependencies are one way that you can create well-defined relationships between a set of modules to form an overall project. Project inheritance is another.Project inheritance lets you define project-wide properties and values that will be inherited by all of the child projects. This is most easily understood by an example.Suppose you are writing a simple web application, which will be deployed both as a traditional web application and as a portlet. One way that you might do this is to define three modules: a core module, containing the application business logic, and two user interface modules, one for each target platform. All three modules would have a common parent POM file, as illustrated in .
Figure : A multi-module Maven project structureLet’s see how you would implement this project structure.Parent POM files are very much like any other POM file. The following listing shows a very simple one:<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany</groupId> <artifactId>myapp</artifactId> <packaging>pom</packaging> <name>Killer application</name> <version>1.0</version> </project>
The main distinguishing factor is the <packaging> element, which is declared as a POM, rather than the WAR or JAR values that we have seen in previous examples. Indeed, all parent POM files must use the pom packaging type.Then, within each child project, you need to declare a <parent> element that refers, suprisingly enough, to the parent POM file:<project> <parent> <groupId>com.mycompany</groupId> <artifactId>myapp</artifactId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>debtcalculator-core</artifactId> ... </project>Note that you don’t need to define the version or groupId of the child project—these values are inherited from the parent.The parent POM file is an excellent place to define project-wide properties or build configuration details. A typical use is to define the Java compile options in one central place. We can set the Java compiler to Java 1.5. This will be inherited by all the children projects, without any special configuration in their POM files:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Creating a Project Template with Archetypes
- InhaltsvorschauEven with a standardized directory structure, it is tiresome to have to create a full set of empty directories by hand whenever you start a new Maven project. To make life easier, Maven 2 provides the archetype plug-in, which builds an empty Maven 2—compatible project template, containing a standard directory structure as well as some sample files illustrating Maven conventions and best practices. This is an excellent way to get a basic project environment up and running quickly. The default archetype model will produce a JAR library project. Several other artifact types are available for other specific project types, including web applications, Maven plug-ins, and others.Let’s take a quick tour to see what you can do with Maven Archetypes. Suppose that we want to create an online store using Maven. Following Maven’s recommendations, we will divide the project into several distinct modules. Our backend module will be called ShopCoreApi:
$ mvn archetype:create -DgroupId=com.acme.shop -DartifactId=ShopCoreApi -Dpackagename=com.acme.shop [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ---------------------------------------------------------------------------- [INFO] Building Maven Default Project [INFO] task-segment: [archetype:create] (aggregator-style) [INFO] ---------------------------------------------------------------------------- ... [INFO] Archetype created in dir: /home/john/dev/projects/shop/ShopCoreApi [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2 seconds [INFO] Finished at: Sun Oct 15 21:50:38 NZDT 2006 [INFO] Final Memory: 4M/8M [INFO] ------------------------------------------------------------------------This will create a complete, correctly structured, working, albeit minimalist, Maven project, including a simple POM file, a sample class, and a unit test. The POM file looks like this:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Compiling Code
- InhaltsvorschauA key part of any development lifecycle is compiling your source code. Compiling your project with Maven is easy—just run mvn compile:
$ mvn compileBefore compiling, Maven will check that all the project’s dependencies have been downloaded, and will fetch any that it doesn’t already have. It also will generate any source code or project resources that need to be generated and instantiate any variables in the resources and configuration files (see ” in ). One of the nice things about Maven is that these tasks are done automatically, as part of the normal Maven lifecycle, without needing any particular configuration.To be sure that there are no stale objects remaining in the target directories, you can also call the clean plug-in, which, as the name indicates, empties the output directories in preparation for a clean build:$ mvn clean compileBy default, Java compilation in Maven 2 supports backward compatibility to JDK 1.3, which means that your generated artifacts will work fine with pretty much any modern version of Java. This is a useful thing to do if you are generating JAR files for community use, or for multiple JDKs. However, if you compile your brand-new Java class full of generics, for example, you’ll get a message like this:[ERROR] BUILD FAILURE [INFO] ---------------------------------------------------------------------------- [INFO] Compilation failure /Users/jfsmart/chapters/caching/app/hibernateCaching/src/main/java/com/wakaleo/ chapters/caching/businessobjects/Country.java:[41,18] generics are not supported in -source 1.3 (try -source 1.5 to enable generics) public Set getAirports() {To get your code to compile correctly using the new Java 5 features (generics and so forth), you need to configure the special maven-compiler-plugin in your pom.xml file. This allows you to define the source and target parameters for the Java compiler. For Java 5, you could do the following:<project...> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plug-ins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> </project>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Your Code
- InhaltsvorschauUnit tests are an important part of any modern development methodology, and they play a key role in the Maven development lifecycle. By default, Maven will not let you package or deploy your application unless all the unit tests succeed. Maven will both JUnit 3.x, JUnit 4 () and TestNG unit tests (see ), as long as they are placed in the
src/testdirectory structure.Running unit tests from Maven is done using the mvn test command, as shown here:$ mvn test [INFO] Scanning for projects... . . . [INFO] [surefire:test] [INFO] Surefire report directory: /home/john/projects/java-power-tools/... /target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.javapowertools.taxcalculator.services.TaxCalculatorTest Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.036 sec Running com.javapowertools.taxcalculator.domain.TaxRateTest Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 sec Results : Tests run: 13, Failures: 0, Errors: 0, Skipped: 0Maven will compile if necessary before running the application’s unit tests. By default, Maven expects unit tests to be placed in thesrc/testdirectory, and will automatically pick up any test classes with names that start or end with “Test” or that end with “TestCase.”Detailed test results are produced in text and XML form in the target/surefire-reports directory. Alternatively, you can generate the test results in HTML form using the surefire reporting feature:$ mvn surefire-report:reportThe HTML report will be generated in a file called
Figure : Unit test results in HTML formtarget/site/surefire-report.html(see ).Another important aspect of unit testing is Test Coverage, which makes sure that a high proportion of your code is actually being exercised by your tests. Although high test coverage is not sufficient in itself to prove that your code is being well tested, the opposite is probably true—poor test coverage is usually a reliable sign of poorly tested code.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Packaging and Deploying Your Application
- InhaltsvorschauOne of the fundamental principles of Maven is that each Maven project generates one, and only one, main artifact. The type of artifact generated by a Maven project is defined in the <packaging> section of the POM file. The main types of packaging are self-explanatory: jar, war, and ear. A typical example is shown here:
<project...> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.accounting</groupId> <artifactId>accounting-webapp</artifactId> <packaging>war</packaging> <version>1.1</version> ...The packaging type will determine exactly how your project is bundled together: compiled classes are placed at the root of a JAR file and in theWEB-INF/classessubdirectory in a WAR file, for example.The next step is to install and/or deploy your application. The install command will generate and deploy your project artifact to the local repository on your local machine, where it will become available to other projects on your machine:$ mvn installThe deploy command will generate and deploy your project artifact to a remote server via one of the supported protocols (SSH2, SFTP, FTP, and external SSH), or simply to a local filesystem:$ mvn deployYour application will be deployed to the remote repository defined in the <distributionManagement> section in your POM file. If you are deploying to a *NIX machine, you will probably need to use one of the network copying protocols: SSH2, SFTP, FTP, or external SSH, as in this example:<distributionManagement> <repository> <id>company.repository</id> <name>Enterprise Maven Repository</name> <url>scp://repo.acme.com/maven</url> </repository> </distributionManagement>If you are deploying to a local filesystem, or to a Windows shared drive, you can use the file URL protocol, as shown here:<distributionManagement> <repository> <id>company.repository</id> <name>Enterprise Maven Repository</name> <url>file:///D:/maven/repo</url> </repository> </distributionManagement>If you need to supply a username and password when you copy to the remote repository, you also will need to provide this information in yourEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Deploying an Application Using Cargo
- InhaltsvorschauThere are also many third-party tools and libraries that can help you deploy your application. One of the most versatile is Cargo. Cargo is a powerful tool that allows you to deploy your application to a number of different application servers, including Tomcat, JBoss, Geronimo, and Weblogic. It integrates well with both Maven and Ant. We don’t have room to explore all of its possibilities here. In this chapter, we will just look at how to configure Cargo to deploy a WAR application to a running remote Tomcat server.Cargo provides a Maven plug-in that allows you to integrate Cargo functionalities smoothly into the Maven lifecycle. The configuration is a bit wordy, mainly as a result of the large degree of flexibity offered by the tool. The full plug-in configuration is shown here:
<plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven2-plugin</artifactId> <executions> <execution> <id>verify-deploy</id> <phase>pre-integration-test</phase> <goals> <goal>deployer-redeploy</goal> </goals> </execution> </executions> <configuration> <container> <containerId>tomcat5x</containerId> <type>remote</type> </container> <configuration> <type>runtime</type> <properties> <cargo.tomcat.manager.url>${tomcat.manager}</cargo.tomcat.manager.url> <cargo.remote.username>${tomcat.manager.username}</cargo.remote.username> <cargo.remote.password>${tomcat.manager.password}</cargo.remote.password> </properties> </configuration> <deployer> <type>remote</type> <deployables> <deployable> <groupId>nz.govt.ird.egst</groupId> <artifactId>egst-web</artifactId> <type>war</type> <pingURL>http://${tomcat.host}:${tomcat.port}/${project.build.finalName} /welcome.do </pingURL> </deployable> </deployables> </deployer> </configuration> </plugin>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Maven in Eclipse
- InhaltsvorschauIf you are using the Eclipse IDE, you can generate a new Eclipse project file (or synchronize an existing one) with a Maven project using the Maven Eclipse plug-in. The simplest approach is often to create a project skeleton using mvn:archetype, and then import this project into Eclipse as a simple Java project. However, Eclipse will not recognise the Maven dependencies without a bit of help. The main purpose of the Maven Eclipse plug-in is to synchronize the Eclipse build path with the dependencies defined in the Maven POM file. For this to work, Eclipse needs to use a classpath variable called M2_REPO, which points to your local Maven repository (see ). You can either set this up manually in Eclipse, or use the Maven plug-in to configure your workspace, using the add-maven-repo goal:
$ mvn -Declipse.workspace=/home/wakaleo/workspace eclipse:add-maven-repoWhen you next open Eclipse, your classpath variables should be set correctly.Next, you need to synchronize your Eclipse project dependencies with the ones defined in your Maven project. To do this, go to your project directory and run the mvn eclipse plug-in:
Figure : Configuring the M2_REPO variable in Eclipse$ mvn eclipse:eclipseThis will update the Eclipse project file with your Maven project dependencies. All you need to do now is simply refresh your project in Eclipse, and the dependencies that you have defined in your Maven project will appear in Eclipse.There is also a plug-in for Eclipse that provides excellent Maven support from within Eclipse itself. The Maven Integration for Eclipse plug-in from Codehaus provides some very useful features in this area. You can install this plug-in using the following remote site:Once installed, you will need to activate Maven Support for the project. Click on the project and select “Maven→Enable Dependency Management.” If a POM file doesn’t already exist for this project, you will be able to create a new one. Otherwise, the existing POM file will be used.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Maven in NetBeans
- InhaltsvorschauFor a long time, Maven support in NetBeans was very limited. However, from NetBeans 6.0 onward, NetBeans provided excellent built-in Maven support, and Maven can now be used as its underlying build tool in the same way as previous versions used Ant. In NetBeans 6, you can add an existing Maven project directly into the workspace or create a new one using one of several Maven archetypes (see ).You also can add dependencies to your POM file using a graphical interface.
Figure : Maven 2 support in NetBeansEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Plug-Ins to Customize the Build Process
- InhaltsvorschauContributed by: Eric RedmondIf there has been one substantial improvement between Maven 1 and Maven 2, it is the simplicity and flexibility of extending the default execution set with custom plug-ins. One can even write plug-ins in other programming languages, such as JRuby, Groovy, or Ant. However, we will turn our focus to the most heavily used and supported default language: Java.A Maven plug-in is a collection of goals and, as mentioned in previous sections of this chapter, a goal is a unit of work in the Maven build lifecycle. Maven comes with tools allowing you to easily create and install your own goals. This allows you to extend the default build lifecycle in any way you can imagine—like Ant tasks if you are so inclined to draw the comparison—but with the benefits of the well-defined lifecycle and network portability of Maven.In order to create a simple plug-in through the archetype, type the following in your command line:
$ mvn archetype:create -DgroupId=my.plugin -DartifactId=maven-my-plugin -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-mojoIn Maven, the implementation of a goal is done in a Mojo—a play on words meaning Maven POJO (Plain Old Java Object) and, well, mojo. All Java Maven Mojos implement theorg.apache.maven.plug-ins.Mojointerface. Without getting too detailed, it is good to understand that Maven is built on the inversion of control (IoC) container/dependency injection (DI) framework called Plexus. If you are familiar with Spring, you are close to understanding Plexus. Plexus is built around the concept that components each play a role, and each role has an implementation. The role name tends to be the fully qualified name of the interface. Like Spring, Plexus components (think Spring beans) are defined in an XML file. In Plexus, that XML file is namedcomponents.xmland lives inMETA-INF/plexus. The consumers of a component need not know the role’s implementation, as that is managed by the Plexus DI framework. When you create your own Mojo implementation, you effectively are creating your own component that implements theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up an Enterprise Repository with Archiva
- InhaltsvorschauContributed by: Eric RedmondA large part of Maven’s power comes from its use of remote repositories. When a project dependency is required or a plug-in is used, Maven’s first task is to reach out to a set of remote repositories, defined in the POM file and/or in the
settings.xml, and download required artifacts to its local repository. This local repository then acts as a local cache. Maven’s Central Repository is a community-driven, open-source set of projects available for download and is accessible by any Maven installation with network access to it. You can browse the repository at . Sometimes your organization will wish to publish its own remote repository, either publicly to the rest of the Internet, or privately in-house. There are two major methods for setting up a repository: either through a dedicated repository manager—such as Archiva or Artifactory—or through a standard server such as Apache HTTP or an FTP server. The latter method is on the wane, so in the next couple of chapters, we will focus on Maven’s recommended repository management tool, Archiva, and one promising contender, Artifactory. First, let’s look at Archiva.Download Archiva from , and unpack the ZIP file to the desired installation location. This location need not have plenty of disk space, because you can set the local repositories to reside on different disks.If you just wish to run the server, select the corresponding operating system directory and run therun.batorrun.shscript. If you are running Windows, you can install Plexus as a service via thebin/windows-x86-32/InstallService.batscript and find a new service installed in your Control Panel’s Services list. For other operating systems, you can use therun.shscript as part of your server startup routine. Alternatively, with a little more effort, you can deploy Archiva onto another web application server such as Tomcat (see ).Once you have the server installed and running, navigate your web browser of choice to . If this is the first time that you are running Archiva, you will be confronted with a screen requesting you to create an admin user (see ). After submission, you can log in as an administrator using the account you just created.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up an Enterprise Repository Using Artifactory
- InhaltsvorschauContributed by: Avneet MangatThe second enterprise repository tool that we will look at is Artifactory. The main purpose of Artifactory is twofold:
- First, it acts as a proxy/cache for any dependencies that you download from repositories on the Internet. This is much faster and more reliable than having each developer download JARs directly from the Internet, and allows some control over which Internet repositories are used by projects in your organization.
- Second, it can be used to store your own enterprise dependencies, or third-party libraries that cannot be published on public repositories (such as JDBC drivers). This makes it much easier for developers to set up new projects, as they don’t need to download and install any JARs manually.
Artifactory is a powerful, well-polished open source tool that provides a number of cool features, including:- A nice AJAX-based web interface, where you can search and browse the repository
- The ability to perform bulk imports and exports of your repository
- Automatic backups of your repository
Let’s take a closer look.To install Artifactory, just download the latest version from the Artifactory web site and extract it to a convenient place. In the following examples, we have installed Artifactory to the/usr/local/artifactorydirectory.Artifactory can be used out of box with little or no configuration. Artifactory comes bundled with a Jetty web server, with default settings that are sufficient for most users. To start Artifactory as a web application inside Jetty, run the batch file or Unix shell script. On Unix, you can use theartifactoryctlscript to start and stop the server:$ /usr/local/artifactory/bin/artifactoryctl startOn Windows, use theartifactory.batscript.You may want to change the default configuration or run Artifactory on under a different server. For example, an organization might have an Apache/Tomcat that it has configured and optimized and that it is comfortable with. In such circumstances, it might be easier and quicker to deploy the artifactory web application directly on the Tomcat server. Another example is if you need to have greater control over sub created in the repository. The rest of this section deals with setting up an Artifactory web application inside a Tomcat server and setting up subrepositories inside the repository.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Ant in Maven
- InhaltsvorschauContributed by: Eric RedmondAnt has had a good run. The past decade has been good for the undisputed Java build tool, but it is time for it to turn in its crown. Ant scripts have a few glaring problems—lack of built-in network portability (you must manually download and install Ant task jars—or perhaps write a script to do it for you); it does not handle dependencies; Ant script size is related to build complexity—i.e., it is procedural, not declarative like Maven; nor does it have any standard concept of a project. Ant is effectively an XML scripting language—Maven would be better described as a comprehensive build scripting platform.But many organizations that wish to convert to Maven have spent considerable resources on creating Ant scripts. Maven has accounted for this and created tools to allow organizations to move forward with Maven, while continuing to utilize their Ant investment. And, to show what good sports that they are, they also have created a toolkit allowing Ant users to utilize some of Maven’s features, such as downloading from remote repositories.The most straightforward way to move from Ant to Maven is to use the existing Ant scripts, wholesale. This can be done by adding the Maven-antrun-plug-in to the POM, and binding it to a phase. What you are actually doing here is embedding Ant code into the POM. However, for the sake of using an existing Ant file, you execute Ant’s
Anttask:<tasks> <ant antfile="${basedir}/build.xml" dir="${basedir}" inheritRefs="true" target="jar"> <property name="ant.proj.version" value="${project.version}" /> </ant> </tasks>diractually defaults to the project’s basedir,antfiledefaults to$basedir/build.xml, andtargetdefaults to the project’s default.inheritRefsdefaults to false, but you may not require them. So if you stick to the defaults, you can get away with something simpler:<tasks> <ant /> </tasks>Assuming that yourbuild.xmlfile executes steps to building a complete project, you may be best served by setting the project packaging type asEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Advanced Archetypes
- InhaltsvorschauContributed by: Eric RedmondArchetypes are a simple and useful way to bootstrap new development across your organization and urge your developers to follow a similar project pattern. Archetypes are a template of a Maven project used to generate skeleton layout for projects of any desired type in a consistent way.The default archetype is called quickstart, and generates a simple project with some “Hello World” Java code and a unit test. Running the
archetype:creategoal as follows:$ mvn archetype:create -DgroupId=com.mycompany -DartifactId=my-proj
will yield a project with the following project structure:my-proj |-- pom.xml `-- src |-- main | `-- java | `-- com | `-- mycompany | `-- App.java `-- test `-- java `-- com `-- mycompany `-- AppTest.javaThe archetype that generates this simple project is outlined by two mechanisms: theMETA-INF/maven/archetype.xmlresource definition file, and the archetype resources under thesrc/main/resources/archetype-resourcesdirectory.maven-quickstart-archetype |-- pom.xml `-- src `-- main `-- resources |-- META-INF | `-- maven | `-- archetype.xml `-- archetype-resources |-- pom.xml `-- src |-- main | `-- java | `-- App.java `-- test `-- java `-- AppTest.javaThere are other archetypes available by default from Maven Central Repository. Check out the list at . At the time of this writing, the following archetypes are supported:- maven-archetype-archetype
- maven-archetype-bundles
- maven-archetype-j2ee-simple
- maven-archetype-marmalade-mojo
- maven-archetype-mojo
- maven-archetype-plugin-site
- maven-archetype-plugin
- maven-archetype-portlet
- maven-archetype-profiles
- maven-archetype-quickstart
- maven-archetype-simple
- maven-archetype-site-simple
- maven-archetype-site
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Assemblies
- InhaltsvorschauContributed by: Eric RedmondMaven is built on the concept of conventions. It does so for a very good reason: if all Maven users follow the same conventions, then all Maven users can navigate and build other Maven-based projects without the need for further training. Tools such as Make and Ant can make no such boast. However, there are cases when the standard conventions cannot apply, for perhaps industrial or technological reasons. With this in mind, the Maven Assembly Plug-in was created.Assemblies in Maven are a collection of files following a certain structure that are packaged for distribution as some artifact, for example, as a zip file. The “structure” is defined through an assembly descriptor xml file, which is pointed to through the project’s POM plug-in configuration and possibly bound to a phase:
<project> ... <build> ... <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptor>src/main/assembly/src.xml</descriptor> </configuration> <executions> <execution> <id>package-source</id> <phase>package</phase> <goals> <goal>attached</goal> </goals> </execution> </executions> </plugin> </plugins> </build>Thefile is an assembly descriptor that packages up the source directory and other files into a zip file suffixed with an ID:src/main/assembly/src.xml<assembly> <id>src</id> <formats> <format>zip</format> </formats> <fileSets> <fileSet> <includes> <include>README*</include> <include>LICENSE*</include> <include>NOTICE*</include> <include>pom.xml</include> </includes> </fileSet> <fileSet> <directory>src</directory> </fileSet> </fileSets> </assembly>The example above has an ID ofsrc. When the package phase is run, it will still create the artifact of the packaging type, for example, thetarget/artifactId-version.jarEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 3: Setting Up Version Control Using CVS
- InhaltsvorschauCVS is a venerable open source version control system first released in the 1980s, one that has a long history in the open source community. Indeed, a great number of open source projects are still hosted under CVS. CVS uses a client-server architecture, with a source code repository residing on a central server. Users connect to the server to download (or “check out,” to use CVS terminology) a copy of the project source code, modify it, and then submit (or “check in”) their changes back to the repository. Several users can work simultaneously on the same file. CVS will attempt to merge the modifications of users as they check in their changes. If it cannot do so for some reason, the user has to resolve the conflict manually. And when the time comes to make a release, users can “tag” a version to be able to retrieve it reliably later on.For some years now, CVS has been showing its age, and it has a number of deep-seated architectural flaws and missing features that make it poorly adapted to Java development projects and the more modern agile development practices in general. For example, it is very difficult to rename or move directories, which makes refactoring cumbersome and difficult. Directory structures in CVS are very rigid—once added, it is very hard to get rid of a directory in the repository. In addition, CVS was designed at a time when most applications consisted entirely of text files, so support for other formats, such as binary files, is limited. The important notion of atomic commits (see ” in ), present in virtually all other modern version control systems, is totally absent from CVS.It should be noted that Subversion (see ) was designed from the ground up to overcome many of the limitations of CVS, something that it has done rather successfully. Subversion is now stable, mature, and technically superior to CVS. If you are given a choice for a new project, it would be wise to consider Subversion seriously.Nevertheless, CVS is still widely used in many organizations, on web sites, and in open source projects, and it is arguably still a tool with which you should be familiar. Thus, rather than being a detailed reference, this chapter is designed more along the lines of a survival guide for Java developers who need to use CVS.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- An Introduction to CVS
- InhaltsvorschauCVS is a venerable open source version control system first released in the 1980s, one that has a long history in the open source community. Indeed, a great number of open source projects are still hosted under CVS. CVS uses a client-server architecture, with a source code repository residing on a central server. Users connect to the server to download (or “check out,” to use CVS terminology) a copy of the project source code, modify it, and then submit (or “check in”) their changes back to the repository. Several users can work simultaneously on the same file. CVS will attempt to merge the modifications of users as they check in their changes. If it cannot do so for some reason, the user has to resolve the conflict manually. And when the time comes to make a release, users can “tag” a version to be able to retrieve it reliably later on.For some years now, CVS has been showing its age, and it has a number of deep-seated architectural flaws and missing features that make it poorly adapted to Java development projects and the more modern agile development practices in general. For example, it is very difficult to rename or move directories, which makes refactoring cumbersome and difficult. Directory structures in CVS are very rigid—once added, it is very hard to get rid of a directory in the repository. In addition, CVS was designed at a time when most applications consisted entirely of text files, so support for other formats, such as binary files, is limited. The important notion of atomic commits (see ” in ), present in virtually all other modern version control systems, is totally absent from CVS.It should be noted that Subversion (see ) was designed from the ground up to overcome many of the limitations of CVS, something that it has done rather successfully. Subversion is now stable, mature, and technically superior to CVS. If you are given a choice for a new project, it would be wise to consider Subversion seriously.Nevertheless, CVS is still widely used in many organizations, on web sites, and in open source projects, and it is arguably still a tool with which you should be familiar. Thus, rather than being a detailed reference, this chapter is designed more along the lines of a survival guide for Java developers who need to use CVS.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Setting Up a CVS Repository
- InhaltsvorschauCVS is essentially a Unix application, although there is an independent fork for Windows called CVSNT. You can run the CVS client virtually anywhere, but the CVS server is most commonly seen in a Unix environment. The first step is to create a directory in which the CVS repository will be stored. In this example, we will place it in the
/usr/local/cvsdirectory, although it can go anywhere you like. For security reasons, it is also a good idea to create a dedicated group for CVS users (say, “cvs”), and to make our new directory belong to this group. You generally need to set up a Unix user account on this machine for each developer that will be using the repository. To be able to check out source code from this repository, users need read access on these files. To be able to commit changes, users need write access.To set up a new CVS repository, you need to run the cvs init command. A CVS repository is essentially stored as a collection of files. The cvs init command sets up the appropriate directory structure and administrative files, which are stored in a directory called CVSROOT, as shown here:# cvs -d /usr/local/cvs init # ls /usr/local/cvs/ CVSROOT # ls /usr/local/cvs/CVSROOT/ checkoutlist cvswrappers,v notify posttag,v taginfo checkoutlist,v Emptydir notify,v postwatch taginfo,v commitinfo history postadmin postwatch,v val-tags commitinfo,v loginfo postadmin,v preproxy verifymsg config loginfo,v postproxy preproxy,v verifymsg,v config,v modules postproxy,v rcsinfo cvswrappers modules,v posttag rcsinfo,v
These are the raw CVS data files. Don’t mess with them directly.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Creating a New Project in CVS
- InhaltsvorschauWhen you start work on a new project, you naturally will want to put it under version control quickly. Importing an empty directory structure or a skeleton project containing only text files into CVS is fairly easy. You may have created a skeleton directory structure manually using a Maven archetype (see ).First, you need to tell CVS where to look for your repository by defining the CVSROOT environment variable. This also will make things easier for the other CVS commands. This variable points to the default CVS repository to be used in all CVS commands. If your CVS repository has been set up on the local machine in
/usr/local/cvs, for example, you might do something like this:$ export CVSROOT=/usr/local/cvsIf you are accessing an organization-wide CVS server across the network, you will probably need to access the repository using thepserverprotocol:$ export CVSROOT=:pserver:john@cvs.mycompany.com:2401/usr/local/cvsWe will talk about thepserverprotocol a bit more later in this chapter.If this repository doesn’t support anonymous access (which is usually the case with an enterprise repository, for example), you will need to login before you can go any further:$ cvs loginIf the server authorizes you to access the repository, your password will be stored locally so that you don’t need to login each time.Next, you import the directory structure using the cvs import command. The import command takes the following form:$ cvs import -m "Initial message" project vendortag releasetagThe project field refers to the directory that you want to store in CVS.You need to provide a text message describing the import. You can either do this using the -m option or let CVS prompt you for a message by opening the default system text editor.You don’t have to worry too much about what you put in the vendortag and releasetag fields, as they are very rarely used in practice.Let’s look at an example. Suppose that our project is a module of an online store called “ShopCoreApi.” To import this project into CVS, just run this command from the project root directory, as illustrated here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking Out a Project
- InhaltsvorschauBefore you can make any changes to the repository, you need to download a local copy of the project source code to your machine. This process is referred to as “checking out” the source code. To do this, run the cvs checkout command (or its shorter version cvs co). Its simplest usable form is illustrated here:
$ cvs checkout -R ShopCoreApi U ShopCoreApi/pom.xml cvs checkout: Updating ShopCoreApi/src cvs checkout: Updating ShopCoreApi/src/main cvs checkout: Updating ShopCoreApi/src/main/java cvs checkout: Updating ShopCoreApi/src/main/java/com cvs checkout: Updating ShopCoreApi/src/main/java/com/acme cvs checkout: Updating ShopCoreApi/src/main/java/com/acme/shop U ShopCoreApi/src/main/java/com/acme/shop/App.java cvs checkout: Updating ShopCoreApi/src/test cvs checkout: Updating ShopCoreApi/src/test/java cvs checkout: Updating ShopCoreApi/src/test/java/com cvs checkout: Updating ShopCoreApi/src/test/java/com/acme cvs checkout: Updating ShopCoreApi/src/test/java/com/acme/shop U ShopCoreApi/src/test/java/com/acme/shop/AppTest.javaFor less typing, you can also use cvs co instead.By default, CVS will not check out any subdirectories. If you want to check out project subdirectories, as you usually will, you need to use the -R option shown above.Like the import command described earlier, cvs checkout expects you to have correctly configured the CVSROOT environment variable. If you are working on the same machine as the CVS repository, you can simply refer to the physical directory path, as shown here:$ export CVSROOT=/usr/local/cvs/You can also provide the repository location on the command line using the -d option:$ cvs -d /usr/local/cvs checkout ShopCoreApi cvs checkout: Updating ShopCoreApi U ShopCoreApi/pom.xml cvs checkout: Updating ShopCoreApi/src cvs checkout: Updating ShopCoreApi/src/config U ShopCoreApi/src/config/hibernate.properties cvs checkout: Updating ShopCoreApi/src/main cvs checkout: Updating ShopCoreApi/src/main/java ...It is more likely, however, that the CVS repository will be on another machine. CVS provides several remote access methods, the most common of which isEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Working with Your Files—Updating and Committing
- InhaltsvorschauIn this section, we will take a guided tour of CVS in everyday life—well, the everyday life of a software developer, in any case. Typically, this will involve updating a local copy of the source code from the repository, doing some work, and, in the process, making some changes to the source code. Then, you will update the repository with the said changes. Let’s look at this process in more detail.Before starting a day’s work on a project, you will usually need to update your local working copy. This allows you to download any modifications that other developers have committed since the last time you talked to the CVS repository. You do this by using the cvs update command. This is straightforward enough, although there are a few options that you should probably use systematically. The -R option processes subdirectories as well as the root directory, which is a must in any modern Java project. The -d option tells CVS to create any missing directories, and the -P option removes any (presumably redundant) empty directories in the directory structure. To update your project, go to the project root directory and run the cvs update command with these options:
$ cd ~/projects/ShopCoreApi $ cvs update -RPd cvs update: Updating . cvs update: Updating src cvs update: Updating src/main cvs update: Updating src/main/java cvs update: Updating src/main/java/com cvs update: Updating src/main/java/com/acme cvs update: Updating src/main/java/com/acme/shop U src/main/java/com/acme/shop/App.java cvs update: Updating src/main/resources U src/main/resources/log4j.properties cvs update: Updating src/test cvs update: Updating src/test/java cvs update: Updating src/test/java/com cvs update: Updating src/test/java/com/acme cvs update: Updating src/test/java/com/acme/shop
Any new or modified files will be indicated by a “U.” In this case, theApp.javafile has been modified, and thelog4j.propertiesfile has been added.Once you have an updated copy of the source code, you can proceed to get some work done. After a while, presumably once you’re coded a little and tested a little, you will be ready to commit your changes to the repository. By this time, someone else also may have updated the repository. To check this, runEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Resolving a Locked Repository
- InhaltsvorschauWhen you commit a file, CVS will place a lock on the file to prevent another user updating the repository at the same time. If you commit your changes at exactly the same time as another user, you might get a message along the following lines:
$ cvs commit -m "Added a log4j.properties file" cvs commit: [04:37:15] waiting for joe's lock in /usr/local/cvs/ShopCoreApi cvs commit: [04:37:45] waiting for joe's lock in /usr/local/cvs/ShopCoreApi ...This is theoretically normal behavior, and the lock should be removed after a short time. Occasionally, however, CVS will maintain the lock when it is no longer needed. If the message persists, check with the user mentioned in the error message (Joe, in this case) to see if the user isn’t currently working with CVS. If he is not, you may need to tidy up the CVS repository manually. To do this, check the directory mentioned in the error message for files whose names start with “#cvs.pfl,” “#cvs.rfl,” “cvs.wfl,” or “#cvs.lock.” If they are present, this directory will be locked by CVS:$ ls /usr/local/cvs/ShopCoreApi/ #cvs.pfl.taronga.14035 #cvs.rfl.taronga.14035 pom.xml,v src vs,vIf you think that this should not be the case, just remove these files. However, if your project has any subdirectories, you will need to remove similar files from those directories, too. For example, on a Unix server, you could do something along the following lines:$ find /usr/local/cvs/ -name "#cvs.*" -exec rm {} \;After that, your commit should work fine.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Working with Keyword Substitution
- InhaltsvorschauCVS provides a feature that lets you replace certain special keyword strings with data provided by CVS. This is often used in file headers, to provide information about the current state and version of the file in CVS. For example your Java files might all start with a header comment block along the following lines:
/* * ... * $Author$ * $Revision$ * Last Modified: $Date$ */
At each commit, CVS will replace these fields with the appropriate values:/* * $Id: TaxCalculator.java 1.5 2007/10/26 23:28:42 john Exp $ * $Author: john$ * $Revision: 1.5$ * Last Modified: $Date: 2007/10/26 23:28:42$ */
There are other keywords as well. The “$Id$” keyword wraps up all of this information into one line:/* * $Id: TaxCalculator.java 1.5 2007/10/26 23:28:42 john Exp $ */
Other common keywords are $Source$, which indicates the path to the file in the version control system, and $Log$, which lists the changes made to the file. The $Log$ keyword is a good one to avoid, as it bloats your source code files with information that can easily be obtained directly from CVS, and can cause unnecessary conflicts during file merges.Keyword substitution is frowned on in some other version control systems, for example, in Subversion. This is mainly because it duplicates information by placing data that should normally be the responsibility of the version control system in the source code files. Occasionally, however, there are valid reasons to do it. If necessary, you can configure Subversion to perform keyword substitution, in particular with the $Id$ keyword. If you are considering a migration to Subversion and use keyword substitution for some mission-critical purpose, the $Id$ keyword may be worth a look.Keyword substitution is not a good idea with binary files, as it will almost certainly corrupt the binary files. We will look at how to deal with this issue in the next section.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Working with Binary Files
- InhaltsvorschauTraditionally, CVS handles binary file formats quite poorly. By default, it will assume that all of your files are text files. As a result, it will try to perform text operations on these files, such as keyword substitution and merging, which inevitably will corrupt your binary file. To avoid this, you need to tell CVS explicitly which of your files are binary.When you add a binary file to an existing project, you can use the -kb option, as shown here:
$ cvs add -kb src/main/webapp/images/logo.png cvs add: scheduling file 'src/main/webapp/images/logo.png' for additionHowever, this is not a particularly practical solution, as you need to remember to do this individually for each binary file. In addition, the cvs import command does not provide this option. A more convenient approach is to use the “cvswrappers” file. This is a special file that lets you tell CVS which options to use for particular file types, based on their extensions.The “cvswrappers” file lives under the CVSROOT project in the CVS repository. To modify this file, you first need to check it out using a standard cvs checkout command:$ mkdir cvsroot $ cd cvsroot $ cvs co CVSROOT cvs checkout: Updating CVSROOT U CVSROOT/checkoutlist U CVSROOT/commitinfo U CVSROOT/config U CVSROOT/cvswrappers U CVSROOT/loginfo U CVSROOT/modules U CVSROOT/notify U CVSROOT/postadmin U CVSROOT/postproxy U CVSROOT/posttag U CVSROOT/postwatch U CVSROOT/preproxy U CVSROOT/rcsinfo U CVSROOT/taginfo U CVSROOT/verifymsg
Now edit the “cvswrappers” file with your favorite text editor. The file is a list of one-line entries. Each entry indicates how a particular file format should be handled in terms of keywork substitution and file merges. For binary files, the lines look something like this:*.gif -k 'b' -m 'COPY'
This tells CVS to treat all GIF files as binary files, with no keyword substitution and no file merging (the -k option refers to keyword substitution, and the -m option tells CVS to create a new copy of the file for each new version, rather than attempting to merge the new file with the previous one). A more complete example of this file is shown here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tags in CVS
- InhaltsvorschauOne of the core features of a version control system is to identify particular versions of your application. You may want to identify a particular test or public release, or an end-of-iteration milestone, or you may want to identify nightly builds. This allows you to return to a known stable version at any time, and it makes it easier to reproduce and fix bugs found in a particular release.In CVS, you use tags to identify a particular version of your application. You do this by running the cvs tag command. The following command, for example, will mark the files in the current working copy as “milestone-iteration-1”:
$ cvs tag milestone-iteration-1 cvs tag: Tagging . T pom.xml cvs tag: Tagging src cvs tag: Tagging src/main cvs tag: Tagging src/main/java cvs tag: Tagging src/main/java/com cvs tag: Tagging src/main/java/com/acme cvs tag: Tagging src/main/java/com/acme/shop cvs tag: Tagging src/main/resources T src/main/resources/applicationContext.xml T src/main/resources/log4j.properties T src/main/resources/messages.properties cvs tag: Tagging src/test cvs tag: Tagging src/test/java cvs tag: Tagging src/test/java/com cvs tag: Tagging src/test/java/com/acme cvs tag: Tagging src/test/java/com/acme/shop T src/test/java/com/acme/shop/AppTest.javaNote that the version that you are tagging here is the version currently in your working directory. This gives you a better control over what version you are actually tagging. If any files have been updated in the repository since you last updated your local copy, they won’t be included in the version that you tag.The syntax for tag labels is fairly strict. Basically, you are not allowed to have spaces, periods, colons, commas, or any other punctuation, with the exception of hyphens (“-”) and underlines (“_”). One reason for this is to avoid confusion with revision numbers.Another common use of tagging is to “promote” a version, putting a special label on a particular version. For example, after user acceptance testing, you might decide to label a particular release candidate as the official production release. You can do this by using theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Creating Branches in CVS
- InhaltsvorschauBranches are an important part of any version control system. In CVS, the main stream of development work is known as the trunk. By default, any changes that you make to the source code will go here. However, developers can create branches, where development is carried out in parallel. This is illustrated in . Typical uses of branches include freezing a production release in one branch while the development team continues to work on the next release in a separate branch. Whenever a production release is made, a new branch is created at this point. If a bug is found in the production release, it can be identified and fixed and a new, stable production version can be released, without interfering with or being affected by the work on the unstable development version.Branches are an integral part of the CVS architecture, and they are closely related to tagging (see ). It is relatively straightforward to create new branches and to merge changes made in one branch into another. To create a new branch from the current working copy, you simply run the cvs tag command using the -b option:
Figure : Branching in CVS$ cvs tag -b production-release-1-0-patchesIt is also a good habit to tag the main trunk when you create a branch. This makes it easier to keep tabs on when branches were made just by looking at the main trunk.From now on, you will be working on the “production-release-1-0-patches” branch, and your changes will not affect anyone who is not working on this branch. Until further notice, all of your commits will go to this branch.Suppose that another developer needs to work on this branch. There are two ways to do this. You can either check out an entirely new working copy for this branch, or you can switch your current working copy to this branch.Checking out a new working copy of the branch in a new directory is a good approach if you need to work on several branches at the same time. For example, you might need to fix bugs in a production release while continuing work on the next version on a separate branch. To do this, you runEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Merging Changes from a Branch
- InhaltsvorschauSuppose that you have fixed a bug in the production release branch, and you now want to incorporate this fix in the main trunk. You do this by merging your corrections back into the main trunk. CVS basically works out what has been modified in your branch since it left the trunk, and applies these changes to the most recent revision in the main trunk.You can do this by updating your main trunk using the -j (for “join”) option. You need an up-to-date working copy of the main development trunk. If you don’t have one on hand, you will need to check out a copy into a new directory. Otherwise, you simply go to your main trunk directory and run cvs update to make sure that you have the latest version:
$ cd ../main_trunk/ShopCoreApi $ cvs update
Once you have an updated working copy of the main development trunk, you need to run the cvs update command again, this time with the -j option. Specify the name of the branch that you want to integrate into the trunk, as shown here:$ cvs update -j production-release-1-0-patches cvs update: Updating ShopCoreApi RCS file: /usr/local/cvs/ShopCoreApi/pom.xml, retrieving revision 1.7 retrieving revision 1.7.2.3 Merging differences between 1.7 and 1.7.2.3 into pom.xml cvs update: Updating ShopCoreApi/src cvs update: Updating ShopCoreApi/src/main cvs update: Updating ShopCoreApi/src/main/java ...When you merge, you should also tag this point in the main trunk as well. This makes it easier to merge further modifications made in your branch in the future. For example, suppose we tag our branch at the point of the merge with a significantly named tag, like “production-release-1-0-patch-1-0,” as shown here:$ cd ../../production-release-branch $ cvs tag production-release-1-0-patch-1-0
Then we continue to work on the production release 1.0 branch, applying the occasional bug fix. The next time that we want to integrate our changes, we only need the changes that have been made since the last merge. You can indicate that you are only interested in the changes that occured from a particular point onward by providing an additionalEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Viewing Change History
- InhaltsvorschauIt often can be useful to know what changes a file, or a project, has undergone over time. The cvs log command lets you review the log messages (which of course everyone has diligently completed) for a particular file or directory, along with useful information such as who made the modifications, when they were made, and so on. For example, we could inspect the changes made to the
log4j.propertiesfile as follows:$ cvs log src/main/resources/log4j.properties RCS file: /usr/local/cvs/ShopCoreApi/src/main/resources/log4j.properties,v Working file: src/main/resources/log4j.properties head: 1.2 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 2; selected revisions: 2 description: ---------------------------- revision 1.2 date: 2007-07-09 00:35:02 +1200; author: wakaleo; state: Exp; lines: +4 -0; commitid: BQCkVlCIqlpjKXos; Made some changes ---------------------------- revision 1.1 date: 2007-07-08 23:19:41 +1200; author: wakaleo; state: Exp; commitid: i5z7fsQ3cUMskXos; Minor modifications =============================================================================There is a lot of information here, most of which you can safely skip over. The most useful information is contained between the lines of dashes at the end of the file.You also can do the same operation on a directory, as shown here for the whole Java source code directory:$ cvs log src/main/java cvs log: Logging src/main/java cvs log: Logging src/main/java/com cvs log: Logging src/main/java/com/acme cvs log: Logging src/main/java/com/acme/shop RCS file: /usr/local/cvs/ShopCoreApi/src/main/java/com/acme/shop/Attic/App.java,v Working file: src/main/java/com/acme/shop/App.java head: 1.3 branch: locks: strict access list: symbolic names: start: 1.1.1.1 vendortag: 1.1.1 keyword substitution: kv total revisions: 4; selected revisions: 4 description: ---------------------------- revision 1.3 date: 2007-07-09 00:35:02 +1200; author: wakaleo; state: dead; lines: +0 -0; commitid: BQCkVlCIqlpjKXos; Made some changes ---------------------------- revision 1.2 date: 2007-07-08 23:19:10 +1200; author: wakaleo; state: Exp; lines: +0 -1; commitid: 6mKyq0emuQLhkXos; Minor modifications ---------------------------- revision 1.1 date: 2007-07-08 23:17:01 +1200; author: wakaleo; state: Exp; commitid: 5lpTqvi8JXQxjXos; branches: 1.1.1; Initial revision ---------------------------- revision 1.1.1.1 date: 2007-07-08 23:17:01 +1200; author: wakaleo; state: Exp; lines: +0 -0; commitid: 5lpTqvi8JXQxjXos; New Project =============================================================================Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Reverting Changes
- InhaltsvorschauIn the previous section, we saw how to examine the change history for a file or directory. But what happens if you realize that a change wasn’t what you wanted? Once you realize that you have committed an incorrect version of a file, it is fairly easy to revert to a previous version. Let’s look at a concrete example. Suppose that you have just committed a new version of the messages.properties file, as shown here:
$ cvs commit -m "Added an important message" cvs commit: Examining . cvs commit: Examining src ... /usr/local/cvs/ShopCoreApi/src/main/resources/messages.properties,v <-- src/main/resources/messages.properties new revision: 1.5; previous revision: 1.4
However, you have a doubt—was this the right version? You can use the cvs diff command to check exactly what changes were made in your last commit as shown here (the -c option gives a slightly more readable output):$ cvs diff -c -r 1.4 -r 1.5 src/main/resources/messages.properties Index: src/main/resources/messages.properties =================================================================== RCS file: /usr/local/cvs/ShopCoreApi/src/main/resources/messages.properties,v retrieving revision 1.4 retrieving revision 1.5 diff -c -r1.4 -r1.5 *** src/main/resources/messages.properties 10 Jul 2007 12:36:14 -0000 1.4 --- src/main/resources/messages.properties 10 Jul 2007 12:36:37 -0000 1.5 *************** *** 1,6 **** # Messages - hello.world=Hello, World! my.message=Welcome to our online shop shopping.cart=Shopping Cart --- 1,6 ---- # Messages my.message=Welcome to our online shop + my.new.message=Utter rubbish! shopping.cart=Shopping CartThe cvs diff command arguably is not the most readable way to compare to files—and generally you are better off using a graphical tool such as the CVS plug-in for Eclipse—but it will do in a pinch. CVS is telling us that we have removed thehello.worldmessage and added a new message (my.new.message), which is, apparently, utter . So, not wanting this to appear in today’s production release, we need to revert to the previous revision.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using CVS in Windows
- InhaltsvorschauThere are several graphical clients that you can use to manage CVS in Windows. Probably the best of these is TortoiseCVS,a graphical CVS client that integrates smoothly into Windows Explorer. TortoiseCVS comes bundled with a CVS client, so users don’t need to install the CVS tool manually. Using TortoiseCVS, you can do pretty much everything you normally would do from the command line, but via a nice graphical interface. This includes checkout modules, updating folders, and committing changes, as well as more sophisticated operations such as tagging, branching, merging, and viewing the CVS logfiles (see ).
Figure : TortoiseCVS in actionEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 4: Setting Up Version Control Using Subversion
- InhaltsvorschauWhen it comes to version control tools, you will often be stuck with whatever happens to be in use in your organization, be it an open source solution like CVS (see ) or one of the many commercial products. However, if you are free to choose your open source version control system (or SCM), Subversion is probably one of the best choices around.Subversion (pronounced “Sub-Version,” for those who are interested in such details) is a relatively new product explicitly designed to overcome the historical shortfalls of CVS (see ) and become the new standard in open source version control tools. It is a superbly engineered piece of software, actively developed and maintained by paid staff from CollabNet. Although it does have a very CVS-ish feel to it, its underlying architecture is quite different, and it has a number of major improvements compared to its venerable predecessor.In this section, we will run through some of the key improvements of Subversion when compared to CVS, and, in doing so, gain some insight into the Subversion architecture and philosophy.Perhaps one of the most profound changes between CVS and Subversion is the way each system keeps track of changes.CVS keeps track of individual file versions. In CVS, when you commit a set of changes, each modified file is updated separately. Tags can be used to identify a snapshot of the repository at a particular point in time. This is illustrated in . Here we have a set of four Java classes. When the developer adds these files to CVS, each will be attributed a version number (1.0). Now our developer makes some modifications to ClassB, ClassC and ClassD, and commits these changes to CVS. Each of these files will be updated on the server and assigned a new version number: 1.1. Our developer may now be so happy with this version that she adds a tag called (imaginatively) “Revision_2.” Each file will be “tagged” with this label, making it easier to fetch from the repository at a later date or from another machine.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- An Introduction to Subversion
- InhaltsvorschauWhen it comes to version control tools, you will often be stuck with whatever happens to be in use in your organization, be it an open source solution like CVS (see ) or one of the many commercial products. However, if you are free to choose your open source version control system (or SCM), Subversion is probably one of the best choices around.Subversion (pronounced “Sub-Version,” for those who are interested in such details) is a relatively new product explicitly designed to overcome the historical shortfalls of CVS (see ) and become the new standard in open source version control tools. It is a superbly engineered piece of software, actively developed and maintained by paid staff from CollabNet. Although it does have a very CVS-ish feel to it, its underlying architecture is quite different, and it has a number of major improvements compared to its venerable predecessor.In this section, we will run through some of the key improvements of Subversion when compared to CVS, and, in doing so, gain some insight into the Subversion architecture and philosophy.Perhaps one of the most profound changes between CVS and Subversion is the way each system keeps track of changes.CVS keeps track of individual file versions. In CVS, when you commit a set of changes, each modified file is updated separately. Tags can be used to identify a snapshot of the repository at a particular point in time. This is illustrated in . Here we have a set of four Java classes. When the developer adds these files to CVS, each will be attributed a version number (1.0). Now our developer makes some modifications to ClassB, ClassC and ClassD, and commits these changes to CVS. Each of these files will be updated on the server and assigned a new version number: 1.1. Our developer may now be so happy with this version that she adds a tag called (imaginatively) “Revision_2.” Each file will be “tagged” with this label, making it easier to fetch from the repository at a later date or from another machine.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Subversion
- InhaltsvorschauWith a bit of luck, you will be able to use a binary installation package adapted to your platform to install Subversion. This is the case with Windows, and also with many distributions of Linux. For Windows, you can download graphical installer packages for both Subversion and the Subversion Python bindings. You will need the Python bindings if you intend to install Trac (see ) as well. Finally, if you intend to run Subversion with Apache on a Windows server, make sure that you use the installer package built against the version of Apache (2.0 or 2.2) that you are using.For many distributions of Linux, there are prebundled installation packages available. On Ubuntu, for example, installing Subversion is as simple as running apt-get install with the appropriate packages:
$ sudo apt-get install subversion libapache2-svnIn some cases, you may need to build and install Subversion yourself. For example, if you want to install Trac on a Linux server, you might need to build Subversion with the correct Python bindings, or you may want to build and install the Apache modules so that you can use Subversion through your Apache server. Be warned that building Subversion from the source code is not always a simple task. The dependencies and configuration options are numerous and complex, and you can find detailed instructions regarding how to do this regarding the Subversion web site. For example, the following listing gives some idea of the steps involved in installing Subversion 1.4.5 on a typical Linux box, with the Apache configuration and Python bindings configured correctly:$ wget http://subversion.tigris.org/downloads/subversion-1.4.5.tar.gz $ tar xvfz subversion-1.4.5.tar.gz $ cd subversion-1.4.5 $ ./configure --prefix=/usr/local --enable-so --enable-rewrite \ --with-susexec --enable-mods-shared=all --with-apr=/usr/local/apache2 \ --with-apr-util=/usr/local/apache2 --with-swig $ make $ make swig-py $ sudo make install $ sudo make install-swig-py
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Subversion Repository Types
- InhaltsvorschauWhen you set up a new Subversion project, the first thing you need to decide is where to store the source code repository, and in what form.In fact, Subversion users have the choice of two quite different storage systems for their repositories: either in a Berkeley DB database, or as a set of flat files. The latter is known in Subversion circles as FSFS because it is effectively a filesystem built on the native filesystem.For the history buffs, this is how this choice came about. In the beginning, Subversion used the Berkeley DB database engine. Berkeley DB is a nice fast, robust, fully transactional open source database designed to be embedded within an application (that is, there is no separate Berkeley DB database to maintain and administer). For many years, all Subversion repositories were based on a Berkeley DB database.The Berkeley DB solution does have a few problems, however.First, it has some major portability and architectural issues. A Berkeley DB database is absolutely not portable from one OS to another: you can’t simply duplicate a Subversion repository created under Linux and have it work under Windows, as you could do with CVS, for instance. Also, according to the Subversion authors, Berkeley DB database repositories don’t work well on a shared network drive, and don’t work at all under Windows 95/98. (OK, I’ve never heard of anyone nowadays setting up an SCM server on a Windows 95 box, but I thought you should know. Anyway…)Second, it may get into trouble if the client suddenly crashes or is brutally cut off. If this happens, the Berkeley DB may end up stuck in an unstable and inaccessible state, and will have to be restored by a system administrator.To get around these problems, the Subversion team released a new storage system in 2004, one based entirely on a flat file structure, and known as FSFS. In an FSFS repository, each revision tree (see “An Introduction to Subversion,” earlier in this section) is stored in a single file, in a special directory dedicated to this purpose. All the revisions are stored in this directory, as shown :Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Setting Up a Subversion Repository
- InhaltsvorschauCreating a Subversion repository is easy. All you need to do is use the svnadmin create command:
$ svnadmin create /path/to/reposBy default, this will set up an FSFS repository (see ) at the specified location. If you really need to, you can use the --fs-type option to specify a Berkeley Database backend:$ svnadmin create --fs-type bdb /path/to/reposTo give a concrete example, here is how I set up the repository for development projects (called, appropriately enough, “dev-repos”) on my home machine, in a dedicated directory called/data/svn:$ svnadmin create /data/svn/dev-reposNote that there is nothing fancy about the repository path: it’s just a plain old OS-dependent file path. On a Windows server, you would do something like this (note the backslashes):C:\> svnadmin create C:\data\svn\dev-reposYou do need to be on the server where your repository is to be stored since it won’t work across a network connection.You rarely have to delve into the details of the repository file structure. One exception is if you need to install some hook scripts, which are scripts triggered by certain repository events, such as creating a new revision. Nevertheless, it can be useful to know a little about how the repository directory is structured. The standard repository structure looks like this:$ ls /data/svn/dev-repos/ conf/ dav/ db/ format hooks/ locks/ README.txtEach directory serves a specific purpose:- conf
- This directory contains repository configuration files, such as the configuration files for svnserve (see ), Subversion’s custom repository server program.
- dav
- This directory is reserved for use by Apache and mod_dav_svn, the Apache module which provides access to the repository using the WebDAV/DeltaV protocol.
- db
- This directory is the real McCoy, the genuine article, the place where all your hours of slaving over a hot keyboard are stored for perpetuity—in other words, this is where the actual repository data is stored. The exact content will vary depending on the repository type, and you shouldn’t have to delve into this directory, except by curiosity. If you have an FSFS (see ) repository, for example, the revisions can be found in the form of numbered files in the db/revs directory, as illustrated here:
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a New Subversion Project
- InhaltsvorschauIn general, a Subversion repository will contain many related projects. In fact, once you have a repository (see ), creating a project in Subversion is also an easy task. However, before starting, you should think about your directory structures.Over time, Subversion users have come up with a number of conventions concerning Subversion directory structures. These conventions actually make good sense, though they may surprise CVS users at first. The key issue is knowing where to store tags and branches. In Subversion, branches and tags (see ) are performed by simply copying the current revision into another directory. So, the recommended Subversion directory structure is to create three subdirectories in the project root directory, as follows:
$ ls trunk/ tags/ branches/The convention involves using each subdirectory for a (fairly obvious) purpose:However, this is just a convention, with the aim of making life simpler for the human users of the repository: Subversion will be happy with any directory structure.- trunk/
- The trunk directory is where the main development work is stored.
- tags/
- The tags directory contains project repository snapshots, identified by human-readable names such as “Release 1.0.”
- branches/
- This directory contains named branches coming off the main development line.
The easiest way to create a new Subversion project is to create the empty directory structure in a temporary directory, and then to import this directory structure into the Subversion repository using the svn import command, as shown here. Subversion naming conventions can be a bit surprising for the uninitiated, so let’s go through the process step-by-step. I start off in my home directory, where I create a temporary directory that I will use to set up an initial empty directory structure:$ pwd /home/john $ mkdir svn-tmp $ cd svn-tmp $ mkdir myproject $ mkdir myproject/trunk $ mkdir myproject/branches $ mkdir myproject/tags
We are going to use the Subversion repository in theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking Out Your Working Copy
- InhaltsvorschauNow that you have created your project in the Subversion repository, you need to obtain a working copy of the project. A common error for new Subversion users is to attempt to use the directory that they just imported as their working directory. This approach is natural, intuitive, and, unfortunately, wrong. Before you can actually use Subversion on your project, you need to obtain a working copy of your project, using the svn checkout command.This is often the first thing you do when you start work on a new project that has been created by someone else, or when you check out the source code from an open source project.First of all, you need to decide where your project is going to live on your disk. In this example, my projects are stored in a directory called projects, which is a subdirectory of my home directory:
$ mkdir ~/projectsNow you need to check out the actual project. Subversion is happy to let you check out any part of the repository you like. You can check out the whole project structure if you want, but you probably really only need one subdirectory: the main development branch (by convention called the trunk). Later on, you might want to create other directories for development branches, but for now, let’s just set up a working copy of the main development branch. To do this, you need to use the svn checkout command:$ svn checkout file:///data/svn/dev-repos/myproject/trunk myproject Checked out revision 1. $ ls myproject/
Subversion just created a directory called myproject in your working directory. Inside, if you look closely, you will see a lone “.svn” directory. This directory contains everything Subversion needs to do its magic, and notably a pristine copy of the revision you checked out, which it will use, for among other purposes, to calculate the changes you’ve made when you commit your modifications to the server:$ ls -al myproject drwxrwxr-x 3 taronga users 4096 2006-05-20 21:15 ./ drwxr-xr-x 125 taronga users 4096 2006-05-20 21:15 ../ drwxrwxr-x 7 taronga users 4096 2006-05-20 21:15 .svn/Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Importing Existing Files into Subversion
- InhaltsvorschauWhen you create a new Subversion repository, you may already have some files that you want to import into your project. This is easy. First, make sure your project directory contains only the files you want to store on Subversion: no compiled files, no temporary files, and so on. Copy this directory into your working folder.Now the tricky thing is to import your new project into the right place in the repository. What you will typically want to do is to import your files into the trunk subdirectory of your new project directory in the repository. So, when you import the files, you have to provide the full path, including the trunk subdirectory. The idea is to do something like the following:
$ svn import newproject file:///data/svn/dev-repos/newproject/trunk -m "Initial Import" Committed revision 3.Here’s an example. In the following listing, I import a new project called JavaLamp into the subversion repository. Now I’ve already cleaned up the directory by removing the compiled classes, and copied the clean project directory into my working directory (~/dev-repos). The project already has some source code, so Subversion builds up the correct structure in the repository and adds all the project files recursively:$ cd ~/dev-repos $ svn import JavaLamp file:///data/svn/dev-repos/javalamp/trunk -m "Initial Import" Adding JavaLamp/src Adding JavaLamp/src/test Adding JavaLamp/src/test/java Adding JavaLamp/src/test/java/com Adding JavaLamp/src/test/java/com/wakaleo Adding JavaLamp/src/test/java/com/wakaleo/jdpt Adding JavaLamp/src/test/java/com/wakaleo/jdpt/javalamp Adding JavaLamp/src/test/java/com/wakaleo/jdpt/javalamp/JavaLampTest.java ... Adding JavaLamp/src/main/resources Adding JavaLamp/src/main/resources/META-INF Adding JavaLamp/src/main/resources/META-INF/MANIFEST.MF Adding JavaLamp/src/main/resources/images Adding (bin) JavaLamp/src/main/resources/images/green-lavalamp.gif Adding (bin) JavaLamp/src/main/resources/images/icon_success_sml.gif Adding (bin) JavaLamp/src/main/resources/images/inqueue.gif Adding (bin) JavaLamp/src/main/resources/images/continuum_logo_75.gif Adding (bin) JavaLamp/src/main/resources/images/icon_error_sml.gif Adding (bin) JavaLamp/src/main/resources/images/building.gif Adding (bin) JavaLamp/src/main/resources/images/buildnow.gif Adding (bin) JavaLamp/src/main/resources/images/checkingout.gif ... Committed revision 4.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Understanding Subversion Repository URLs
- InhaltsvorschauIn Subversion, you use a special form of URLs to access repositories. Subversion repositories can be accessed through a variety of protocols, and it can come in handy to understand some of the finer points. This section describes the different protocols and how to use them.The various types of Subversion URLs are listed in .
Table : Subversion URLs Examples Description file:/// file:///data/svn/dev-repos or file:///d:/svn/dev-repos Direct access to a repository on a local disk. This is the only form that varies depending on the underlying OS. http:// http://svn.mycompany.com:9834/repos Access to a repository over a standard HTTP connection using the WebDAV/DeltaV protocol. Behind the scenes you will find a →Subversion-friendly Apache server fitted out with a mod_dav_svn module (see ). This protocol has obvious advantages when it comes to crossing corporate firewalls and so forth. https:// https://svn.mycompany.com:9834/repos Access to a repository over a secured HTTPS connection, also using a Subversion-friendly Apache server. svn:// svn://svn.mycompany.com:3690/dev-repos Access to a repository via the proprietary protocol used by svnserve server (see ). svn+ssh:// svn+ssh://svn.mycompany.com:3690/dev-repos Secured access to a repository via the proprietary protocol used by svnserve server, with SSH tunnelling. The HTTP and svn-based URLs are pretty standard, and allow you to specify host names, and ports, as you would expect. Using the file:/// URLs is a bit more specific because you are dealing with a physical directory path on the local machine. You may have wondered why there are three slashes (“///”) at the start instead of two, as is the case with the other URL forms. This is because you are actually allowed to put a hostname in the URL, as with the other URL forms (e.g., file://localhost/data/svn/dev-repos). However, the only authorised hostname is “localhost,” so people tend to leave it out.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Working with Your Files
- InhaltsvorschauAs with many tools, Subversion provides a rich set of functionalities. In practice, however, you can get by quite well if you only know a few key functions, which you will need on a daily basis. This article takes you through what you need to know to get by with Subversion in your everyday work.No man is an island, as someone once said. Or, in this case, (almost) no developer works in isolation. If, like most of us, you work in a team, the first thing you will need to do before you start working is to update your local files with any changes made by your fellow developers. To do this, you run the svn update command:
$ svn update U src\main\java\com\wakaleo\jdpt\javalamp\plugins\ContinuumPlugin.java D src\main\resources\ApplicationResources.properties A src\main\resources\images\icon_success_sml.gif A src\main\resources\images\icon_error_sml.gif A src\main\resources\images\icon_warning_sml.gif Updated to revision 7.Subversion updates your local files, and gives you a summary of the modifications which it has performed. All of the files affected by changes in the repository are listed, each with a code indicating the type of modification. The main codes you will see, if all goes well, are the following:- A
- A new file has been added to the repository, and has been successfully transferred to your local working copy.
- D
- A file has been deleted from the repository, and, as a result, from your local working copy.
- U
- A file has been modified (updated) in the repository, and the modification has been correctly updated in your local working copy.
Updates do not always go according to plan, however. Suppose you do some work on your local copy, and then perform an update:$ svn update G src\main\java\com\wakaleo\jdpt\javalamp\plugins\ContinuumPlugin.java C src\main\java\com\wakaleo\jdpt\javalamp\JavaLamp.java Updated to revision 10.There are some new codes here. Let’s see what they mean:- G
- Subversion has detected some modifications to this file on the server. However, Subversion was able to merge (merGe) them together and update your local copy with the new modifications, so you should be all right. This new file exists only in your local copy at the moment; you’ll need to commit it later to update the .
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Seeing Where You’re At: The Status Command
- InhaltsvorschauThe svn status command is one of the more useful commands in the Subversion toolbox. In a nutshell, it lets you know what you’ve modified since the last time you synchronized your working copy with the latest version in the Subversion repository. This is a useful thing to do before committing your changes, since it gives you a quick summary of the work you’ve done, and it can help you to remember everything in the comment when you commit. A typical example is the following:
$ svn status M ch04.xml A figs/subversion-revisions.gif D figs/subversion-no-longer-used.gif ? ch04.htmlIn this example, one file has been modified since the last file (M), one new file is to be added (A), and one is to be deleted (D). The question mark (?) indicates a file not currently under version control (see for a way to hide files that you don’t want to appear in the status output). This is what you will use in most situations, but in fact the svn status command can tell you much, much more. Try svn status --help for a detailed description of the command.You can also use this command to keep track of renamed or moved directories, when you use svn move or svn copy. This is best explained by an example. In the following listing, I rename a directory called resources as dist. This directory contains two files, INSTALL and README. Now, when I run svn status, I get something along the following lines:$ svn move resources/ dist A dist D resources/INSTALL D resources/README D resources $ svn status -v 27 27 taronga . 27 24 taronga ch25.xml 33 33 taronga ch04.xml A + - 33 taronga dist + - 33 taronga dist/INSTALL + - 33 taronga dist/README ... D 33 33 taronga resources D 33 33 taronga resources/INSTALL D 33 33 taronga resources/README ...
Astute readers will notice several things here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Resolving Conflicts
- InhaltsvorschauWhen two users modify the same file, Subversion will try to merge the changes together. This often works well enough if the modifications occur in different parts of the file. If the modifications affect the same lines of code, or if the file in question is a binary file, Subversion may well throw in the towel and let you sort it out (see for more on how to use file locking to avoid conflicts with binary files). In this case, you’re on your own. Well, not quite. Subversion does give you some tools to work with.Let’s work through an example. Suppose you want to update the INSTALL.txt file, to give it a sensible heading like the one shown here:
Installation instructions for the JavaLamp application ------------------------------------------------------
So you make the change to your local copy and commit. Often, this is the first warning of trouble you get: Subversion refuses your commit because someone else has already committed a more recent version since your last update:$ svn commit -m "Added a nice new title for the installation instructions." Sending INSTALL.txt svn: Commit failed (details follow): svn: Out of date: '/trunk/INSTALL.txt' in transaction '21-1'Fair enough, you might say, so let’s update:$ svn update C INSTALL.txt G src\main\java\com\wakaleo\jdpt\javalamp\plugins\ContinuumPlugin.java Updated to revision 21.At this stage, Subversion can often fix the problem on its own by merging the two versions. In this case, two modifications could have caused a conflict, but one was successfully merged (“G”) by Subversion. The “C” next to INSTALL.txt, on the other hand, indicates an unresolved conflict. Now it’s up to you to fix it. In the unlikely event that you forget to resolve the conflict, Subversion will just refuse to commit until you fix the problem:$ svn commit svn: Commit failed (details follow): svn: Aborting commit: 'C:\dev\AnotherJavaLamp\INSTALL.txt' remains in conflictSo, now we need to take a look at the file:<<<<<<< .mine Installation instructions for the JavaLamp application ------------------------------------------------------ ======= JavaLamp Installation instructions ---------------------------------- >>>>>>> .r21
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Tags, Branches, and Merges
- InhaltsvorschauBranching and tagging are central concepts in Configuration Management theory. Branching allows us parallel development work to be done on a separate set of files, while allowing work to continue on the main development line without interference. This is typically done in software projects near a release date, where one part of the team will work on bug fixes and code stabilization on the main development branch (or “trunk,” in Subversion terms), while another part of the team continues working on new modules, without putting the upcoming release version at risk. After the release, the new modules must be reintegrated (or “merged”) back into the principal development line.A tag is simply a way of assigning a human-readable label to a given version (or, in Subversion terms, “Revision”) of a project. This is useful when you need to identify releases of your product for future reference.Unlike most other SCM systems, Subversion has no distinct notion of branches or tags; for Subversion, both branches and tags are simply repository copies. As we will see, this has some advantages and some disadvantages.Creating a new branch or tag is essentially the same operation in Subversion: you just copy the directory you want to branch or tag. And with Subversion, copying directories is fast and cheap, since you are essentially creating the Subversion equivalent of a symbolic link to the original revision.In CVS, for example, each file must be individually tagged, one by one. Experienced CVS users will know the pain of tagging a big projects, where you can go off for a coffee, pizza, or even a four-course meal while waiting for the tagging to finish. In Subversion, by contrast, tagging is pretty much instantaneous. The most efficient way of doing this is copying the repositories on the server, as shown here:
$ svn copy -m "Tagging Release 1.0" file:///data/svn/dev-repos/javalamp/trunk file:///data/svn/dev-repos/javalamp/tags/release-1.0 Committed revision 57. $ svn list file:///data/svn/dev-repos/javalamp/tags
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Rolling Back to a Previous Revision
- InhaltsvorschauOne important thing that you often need to do with a version control system is to undo changes by rolling back to a previous version. Suppose, for example, that you spend a few days working on your favorite project. You make some changes, add some files, and so on. You commit your changes into the code repository several times as you go. Then, in a flash of genius, you realize that you’ve got it all wrong, irreparably wrong. (As a rule, this flash of genius will arrive around 4 p.m. on a Friday, before a planned demonstration on Monday.) The only thing to do is to rollback your changes and return to a previous, stable version.So how do you do this in Subversion? The simplest way is to use the svn merge command (see ) to undo your changes. The trick is to merge backward, so that the older version takes precedence over the more recent (incorrect) changes.Let’s go through an example.First, I add a class to a project, delete another, and modify yet another class. Here’s what happens when I commit these changes:
$ svn commit -m "Changes we will regret later" Sending src\main\java\com\wakaleo\maven\plugin\schemaspy\SchemaSpyReport.java Adding src\main\java\com\wakaleo\maven\plugin\schemaspy\util\DatabaseHelper. java Deleting src\main\java\com\wakaleo\maven\plugin\schemaspy\util\JDBCHelper.java Transmitting file data .. Committed revision 11.So far so good. However, some time (and several revisions) later, I decide that these changes are going nowhere. I want to go back to good old revision 10, and start over again. To do this, just merge backward, starting with revision 14, back to revision 10. In practice, this will undo each change done since revision 10, which, it so happens, is exactly what we want:$ svn merge -r 14:10 https://wakaleo.devguard.com/svn/maven-plugins /maven-schemaspy-plugin/trunk U src\main\java\com\wakaleo\maven\plugin\schemaspy\SchemaSpyReport.java D src\main\java\com\wakaleo\maven\plugin\schemaspy\util\DatabaseHelper.java A src\main\java\com\wakaleo\maven\plugin\schemaspy\util\JDBCHelper.javaEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using File Locking with Binary Files
- InhaltsvorschauIn any development project of any size, there will be times when two people want to modify the same file at the same time. The risk is that one developer may overwrite the changes of the other, which has the potential to cause delays, lost code, and unnecessary bloodshed among irate team members. In the version control world, there are two schools as to how to deal with this.
- Using file locking
- In this approach, only one person can modify a given file at any given time. When a user checks out a file, it becomes unmodifiable for all other users. This approach guarantees that your changes will not be overwritten inadvertently by another user, but potentially at the cost of slowing down development work by making it impossible for more than one developer to work on a given file at the same time. Indeed, there are many cases in which it is quite legitimate for several users to modify the same file simultaneously; for example, adding distinct localised messages into the same properties file. File locking can also create maintenance headaches, as files may become or remain locked unnecessarily, for instance, if a user goes on vacation without checking in his work.
- Using file merging
- File merging is a more flexible approach. Any number of users can check out a local copy of a file in the repository, and modify it on their machine. The first user to complete his or her modifications updates the repository. When the next developer tries to commit his or her changes, they will be informed that the file has been updated on the repository, and needs to be updated (see ). So, they update their local copy of the file. If the changes were in different parts of the code, Subversion can merge the two versions automatically. If the changes modify the same lines of code, it’s up to the user to sort them out manually (see ).
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Breaking and Stealing Locks
- InhaltsvorschauAdministrators of course have absolute power over their repositories, and a minor measure such as locking files will not resist a determined administrator very long. There are occasionally times when a user forgets to commit their modifications before going on holidays. In situations such as this, the administrator may have to intervene and manually unlock the file.The first step is to verify which files have been locked, and by whom. The svnadmin lslocks lists the active locks on a given repository:
$ svnadmin lslocks /data/svn/dev-repos Path: /trunk/images/product-logo.gif UUID Token: opaquelocktoken:f9366d36-8714-0410-8278-b1076b57a982 Owner: taronga Created: 2006-05-24 23:41:13 +1200 (Wed, 24 May 2006) Expires: Comment (0 lines):The administrator can use the svnadmin rmlocks command to manually remove any offending locks, as shown here:$ svnadmin rmlocks /data/svn/dev-repos /trunk/images/product-logo.gif Removed lock on '/trunk/images/product-logo.gif'.The other way to get around an unwanted lock is to steal it. This is, of course, terribly unethical, and so should be used with caution. But if you really need to (say, your administrator went on vacation, too), here’s how you do it.Suppose that your boss just asked you to modify the product logo, as it needs a few more bright yellow stripes. You better lock the file before you do any work on it:$ svn lock images/product-logo.gif svn: Path '/trunk/images/product-logo.gif' is already locked by user 'bill' in filesystem '/data/svn/dev-repos/db'Uh oh, looks like Bill’s forgotten to commit his changes before he left for Fiji last week. OK, we’ll just unlock it ourselves:$ svn unlock file:///data/svn/java-power-tools/trunk/images/product-logo.gif svn: User 'taronga' is trying to use a lock owned by 'bill' in filesystem '/data/svn/dev-repos/db'OK, no more Mister Nice Guy. If we use the --force, we can move just about anything (apologies to Yoda):$ svn unlock --force file:///data/svn/java-power-tools/trunk/images/product-logo.gif 'product-logo.gif' unlocked.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Making Locked Files Read-Only with the svn:needs-lock Property
- InhaltsvorschauUnlike more strict lock-based version control systems that physically prevent users from modifying a locked file, even on their own machines, the default Subversion approach to locking is not foolproof. If a user locks a file, she is guaranteed that it will remain untouched by other users (except perhaps for the occasional mad administrator…). However, there is still the risk that another user may work for three days to redo your product logo in a brilliant new shade of purple, only to discover that the file has been locked by the developer down the corridor. One way to avoid this is to convince users to check the status and to lock binary files before starting any work on them. If this practice is respected, the system is quite robust. However, it does require some discipline, and it may be difficult to enforce.Another approach is to use a special property, svn:needs-lock. All you have to do is to assign this property to a file (any value will do), and it will become read-only. To make it read-write, a user needs to lock the file. When the lock is released, the file becomes read-only again:
$ cd ~john/projects/javalamp $ svn propset svn:needs-lock 'true' images/product-logo.gif property 'svn:needs-lock' set on 'images/product-logo.gif' $ svn commit -m "This is a binary file which needs to be locked to modify." Transmitting file data . Committed revision 40. $ svn update At revision 40. $ ls -al images/product-logo.gif -r-xr-xr-x 1 taronga users 12943 2006-05-22 19:14 images/product-logo.gif
Here we have updated the property and committed the change to the repository. Note that when you set a property on a file, you refer to the local copy of the file. In the above example, we did this from the project root, but you can actually do it from any directory.Once we update our local work copy; the file is placed in read-only mode. If we want to modify the file, we need to lock it first:$ svn lock images/product-logo.gif 'product-logo.gif' locked by user 'taronga'. $ ls -al images/product-logo.gif
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Properties
- InhaltsvorschauOne of the more innovative aspects of Subversion is the ability to assign and version metadata that you associate with files or directories. This metadata can take the form of just about anything you want, from simple text values to binary objects. The possibilities are virtually unlimited, and you can have a great deal of fun building complicated (and sometimes incomprehensible) build scripts using custom properties.However, without going this far, there is a lot of added value to be had simply by using standard out-of-the-box Subversion properties. In this section, we will look at some of the more useful built-in Subversion properties.On Unix systems, you can define a file as an “executable,” meaning it can be executed directly from the command line. The concept does not exist in other operating systems such as Windows. Subversion provides the svn:executable property to cater to this. If you assign a value (any value) to this property for a given file, Subversion will make this file executable whenever you check it out in a compatible operating system:
$ svn propset svn:executable "true" INSTALL property 'svn:executable' set on 'INSTALL'The svn:mime-type property plays an important role in Subversion. Although Subversion stores all files internally as binary files for performance reasons, Subversion needs to distinguish between text files, which can be merged, and true binary files, which can’t. When you add a file, Subversion will try to automatically detect any binary files you add. Subversion will assign the value “application/octet-stream” to the svn:mime-type property for these files. You can always use the svn propget command to verify their mime-type:$ svn add * Adding (bin) icon.jpg Adding (bin) plan.mpp Adding (bin) stakeholders.xls $ svn propget svn:mime-type * icon.jpg - application/octet-stream plan.mpp - application/octet-stream stakeholders.xls - application/octet-stream
There are two main uses for this property. First, Subversion will not attempt to merge changes in binary files. When you update your working copy, if there is a newer version of a binary file on the server, it will simply replace your current one. If you have modified the file locally, Subversion will generate three distinct files, and place the file in an unresolved status, as shown below. It is up to you to decide which copy is correct, and to resolve the conflict manually usingEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Change History in Subversion: Logging and Blaming
- InhaltsvorschauSubversion has a couple of handy ways of checking the change history of a file or .The svn log command can be used to obtain a short history of all changes ever done to the repository:
$ svn log ------------------------------------------------------------------------ r427 | john | 2007-04-11 16:42:09 +1200 (Wed, 11 Apr 2007) | 1 line Added draft contributions for SchemaSpy material ------------------------------------------------------------------------ r426 | john | 2007-04-04 16:41:48 +1200 (Wed, 04 Apr 2007) | 1 line ...If you add the “-v” (--verbose) option, you get a detailed summary of each commit, including the files modified in each commit:$ svn log -v ------------------------------------------------------------------------ r427 | john | 2007-04-11 16:42:09 +1200 (Wed, 11 Apr 2007) | 1 line Changed paths: A /java-power-tools/trunk/src/ch-XX-schemaspy.xml D /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/image-1.png D /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/image-2.png D /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/image-3.png A /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/schemaspy-image-1.png A /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/schemaspy-image-2.png A /java-power-tools/trunk/src/contrib/kalali/SchemaSpy/schemaspy-image-3.png ...And if you need to narrow the history to a single revision, or a small range of revisions, you can use the -r (--revision) option. Here are a few examples:$ svn log -vr HEAD # What was modified in the latest version on the server? $ svn log -vr {2006-05-25} # What was modified in the last revision before the 25th of May 2006? $ svn log svn log -vr 10:15 # What was modified between revisions 10 and 15
If you run svn log from within a particular directory, it will only list the revisions in which a file in this directory (or one of its subdirectories) has been modified:$ cd ~projects/java-power-tools/src/sample-code/spitfire $ svn log ------------------------------------------------------------------------ r436 | john | 2007-05-07 12:47:23 +1200 (Mon, 07 May 2007) | 1 line ------------------------------------------------------------------------ r410 | john | 2007-03-24 13:14:55 +1200 (Sat, 24 Mar 2007) | 1 line ------------------------------------------------------------------------ r406 | john | 2007-03-19 07:36:14 +1200 (Mon, 19 Mar 2007) | 1 line ...
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a Subversion Server with svnserve
- InhaltsvorschauSubversion also comes with a lightweight server called svnserve, which can be used to provide network access via the proprietary svn and svn+ssl protocols. Svnserve is easier to configure and offers higher performance than accessing Subversion via Apache. It works equally well in both Windows and *NIX environments.You can start up the svnserve as a standalone server process from the command line, as follows:
$ svnserve --daemon --root /data/svnThe --daemon (or -d) option tells svnserve run as a background process. The --root (or -r) option lets you specify the repository root path. If provided, URL paths will be relative to this directory. Although optional, it is good practice to provide this path, because in this case the svnserve process cannot access anywhere outside this path.The default port is 3690, but if you need to change this, you can use the --listen-port option. The --listen-host can be used to define the interface that svnserve listens on. You can use either a regular hostname or an IP address.Logically enough, the svnserve process needs to have read/write access on this directory. One way of doing this is to create a user and a group, both called svn. This user should have ownership of the Subversion repository directory. You could do this, as root, as follows:# chown -R svn:svn /data/svn # chmod -R 775 /data/svn
Then execute svnserve as this user.You can do a quick check to see if the server is functioning correctly by running svn list using a svn URL, as shown here:$ svn list svn://localhost/dev-repos branches/ trunk/Of course, on a real server, you would typically set up the svnserve process to run as a service. This varies from system to system, and if you’ve gotten this far, you’re probably big enough to work this out for yourself.Now that svnserve is running, you can now check out a project:$ svn co svn://localhost/dev-repos/javalamp/trunk JavaLampRemote A remoteJavaLamp\INSTALL.txt A remoteJavaLamp\src A remoteJavaLamp\src\test A remoteJavaLamp\src\test\java A remoteJavaLamp\src\test\java\com A remoteJavaLamp\src\test\java\com\wakaleo A remoteJavaLamp\src\test\java\com\wakaleo\jdpt ...Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a Secure svnserve Server
- InhaltsvorschauThe svnserve server is not a particularly security-oriented solution: in particular, file contents are sent over the network in unsecured form, and passwords are stored in unencrypted form on the server. As such, it is not well suited to publishing a repository over an unsecured network or over the Internet, for example. If security is an issue in your environment, it is worth knowing that svnserve supports SSH tunneling via the
protocol.svn+ssh
To make svnserve use SSH tunneling, you simply use the “svn+ssh” from the client machine. No demon process needs to be running on the server: instead, an SSH tunnel is created for each server access and the svnserve command is run on the server machine through the SSH tunnel. When SSH tunneling is used, svnserve does not use the password database that we saw earlier (see ). Instead, users need to have a physical account on the server, with authorization to login to the repository server using SSH.User rights are determined by physical access to the repository directory: a user with read access to a repository directory on the server will only have read access to the repository, whereas a user with read-wight access to a repository directory will have read and write access to the repository. An easy (if rudimentary) way to do this is to define read-write access to the Subversion repository for the “svn” group. This way, members of the “svn” group have read and write access to the repository, whereas all other users have read-only access.Whenever you access the server, you will be prompted for a password, as illustrated here:$ svn list svn+ssh://svnserver.mycompany.com//data/svn/dev-repos/myproject Password: branches/ tags/ trunk/Note that, by default, the full repository path must be provided (like when we used the “file:” protocol).Accessing an svnserve server through SSH tunneling is pretty simple to set up, and is certainly secure. On the down side, it is not particularly flexible, and doesn’t allow the fine-grained access rules you get with the other network access mechanisms. The ssh+svn tunneling mechanism also has an annoying habit of re-requesting passwords they are not cached on the local machine, and a new ssh tunnel is created for each operation.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a WebDAV/DeltaV Enabled Subversion Server
- InhaltsvorschauIf you want to provide access to your Subversion repository via Internet, setting up WebDAV access may well be the solution of choice. WebDAV (Web-based Distributed Authoring and Versioning) is an extension of the HTTP protocol designed to allow users to collaboratively create and modify documents on a remote server. DeltaV is an extension of WebDAV, which provides versioning and version control services. WebDAV access has many things going for it: it is standard, widely used, well supported by many operating systems, and is based on the very ubiquitous HTTP/HTTPS protocol. It is widely supported by most modern operating systems (, for example, shows a WebDAV access to the repository of one of the Apache projects under KDE in Linux).Subversion uses the robust Apache web server to provide repository access via WebDAV/DeltaV. Here, we’ll see how to set up an Apache server to do this.
Figure : A WebDAV connection to the Apache Maven Continuum repository viewed from a Linux clientFirst, you will need an installation of Apache 2.0 or better, with the mod_dav module (mod_dav is included with most distributions of Apache nowadays; if not, you may have to compile and install Apache with the right configuration options yourself). You also need the mod_dav_svn module, which is Subversion’s mod_dav plug-in. If Apache is already installed when you install Subversion, the Subversion installer will have a go at updating the Apache configuration files correctly. Still, it is wise to check. The Apache configuration file is called httpd.conf, and its exact location will vary depending on your OS and how Apache was installed. On Unix machines, it can often be found at /usr/local/apache2/conf/httpd.conf or /etc/apache2/http.conf. On Windows machines, you will usually find it in the Apache installation directory (something like C:\Program Files\Apache Group\Apache2\conf).To access Subversion via Apache, you will need the mod_dav_svn and mod_authz_svn modules to be loaded with aEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a Secure WebDAV/DeltaV Server
- InhaltsvorschauAlthough Digest authentication provides a reasonable level of security, and is often quite sufficient for a secured enterprise network or VPN, for example. However, it will not resist the efforts of a determined hacker, and is not suitable if you need to sensitive data across the Internet. True security in a web environment requires HTTPS encryption. Configuring Apache SSL configuration and managing SSL certificates is beyond the scope of this book, but is well documented elsewhere, such as on the Apache web site.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Customizing Subversion with Hook Scripts
- InhaltsvorschauSubversion is a very flexible tool, and it is easy to extend its features to suit your particular environment. For example, you might want to send a mail on each commit, update your issue tracking system, or even to force users to include a reference to a bug number in their commit message.Subversion hooks allow you to do this sort of thing. Subversion hooks allow you to trigger arbitrary actions whenever a particular event (typically a commit) occurs in the repository. The hook scripts live in the hooks directory of your Subversion repository. When you install Subversion, there will be a number of sample scripts already provided, indicated by the “.tmpl” suffix:
# cd /data/svn/repos # ls conf dav db format hooks locks README.txt # ls hooks post-commit.tmpl post-revprop-change.tmpl pre-commit.tmpl pre-revprop-change.tmpl start-commit.tmpl post-lock.tmpl post-unlock.tmpl pre-lock.tmpl pre-unlock.tmpl
The events are fairly intuitive. For example, the pre-commit script is run just before a commit happens, and the post-commit just afterward. Subversion supports five hooks, which are each used for different purposes:- start-commit
- This script is run just before a commit transaction begins, and is typically used to prevent a commit from happening at all if some condition is not met. If this script returns a nonzero result, the commit will not take place.
- pre-commit
- This script is run when the commit is ready to happen, but just before it actually takes place. You can use this script to enforce constraints; you might want to require that developers include a reference to an issue in your issue tracking system in the commit message, for example. If this script returns a nonzero result, the commit will be aborted.
- post-commit
- This script is invoked after the transaction has been processed and once a new revision has been created. You would typically use this script to send notification messages or to update your issue tracking system, for example.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing Subversion As a Windows Service
- InhaltsvorschauIf you have to install Subversion on a Windows server, you’ll probably want to install it as a service. Unfortunately, this feature doesn’t come out of the box with the Windows Subversion installation package: you have to set it up yourself.Magnus Norddahl has written a small Windows service wrapper utility called , which can be used to install Subversion as a Windows service. Download the package, place the executable file (SVNService.exe) in the same directory as your subversion executable, and set up the service as follows:
C:\> SVNService -install -d -r d:\svn-repositoryAn alternative approach is to use the generic Windows InstSrv and SrvAny tools, which come with the Windows Server Resource Toolkits. First, you need to set up a new Windows service by running InstSrv. You need to provide the name of the service you want to create (say “svnserve,” just to be original), and the full path of the Srvany.exe:C:\>InstSrv svnserve "C:\Program Files\Windows Resource Kits\Tools\srvany.exe" The service was successfuly added! Make sure that you go into the Control Panel and use the Services applet to change the Account Name and Password that this newly installed service will use for its Security Context.Now you need to configure the service in the Windows registry (see ). Open the Registry Editor, go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\svnserve, add a Parameters Key and two String Values:- Application, which indicates the full path to your svnserve executable
- AppParameters, in which you place the command-line parameters you would normally pass to svnserve (such as “-d -r D:\svn\repository”)
Now you should have a new Windows service. By default, the service will start up automatically when you reboot the server. You can start (or stop) the service manually in the Services administration screen (see ) or using the NET START and NET STOP commands at the command line, as follows:
Figure : Setting up Subversion as a Windows serviceEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Backing Up and Restoring a Subversion Repository
- InhaltsvorschauIf you are in charge of administering a Subversion repository; it can come in handy to know how to back it up. The svnadmin dump command lets you do just that: it generates a portable (although not very readable) format that you can use to restore your precious repository if disaster strikes. This is also recommended procedure when upgrading to a new version of Subversion.The following line will dump the whole repository into a file called svn-backup:
$ svnadmin dump /data/svn/dev-repos > svn-backup. * Dumped revision 0. * Dumped revision 1. * Dumped revision 2. ... * Dumped revision 30. * Dumped revision 31.Of course, you can gain some space (I gain around 250n my repository) by compressing the backup format with a tool like gzip:$ svnadmin dump /data/svn/dev-repos | gzip -9 > svn-backup.gzTo rebuild your repository after a disaster, or after an upgrade, you need to recreate your repository and then load the backed-up repository data using svnadmin load:$ svnadmin create /data/svn/dev-repos svn-backup $ svnadmin load /data/svn/dev-repos < <<< Started new transaction, based on original revision 1 * adding path : book.xml ... done. * adding path : ch04.xml ... done. * adding path : ch25.xml ... done. ------- Committed revision 1 >>> <<< Started new transaction, based on original revision 2 ...
If you compressed the backup file as shown above, you can also restore the repository directly from the compressed file as shown here:$ gunzip -c svn-backup.gz | svnadmin load /data/svn/dev-reposEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Subversion in Eclipse
- InhaltsvorschauSubversion integrates quite smoothly into the Eclipse IDE. There are currently two open source plug-ins available providing Subversion integration in Eclipse. It is worth noting that there is also an Eclipse project (in incubation at the time of writing) aiming at provided integrated Subversion support in Eclipse (see ). However, at the time of this writing, Subversive is younger and less mature than Subclipse, so we will focus on Subclipse.You install Subclipse from the Subclipse update site at http://subclipse.tigris.org/update_1.2.x for Eclipse 3.2 or higher, in the usual way via the Install/Update window (see ).
Figure : Installing SubclipseDepending on your OS, you may need to do some configuration in the Preferences screen (see ). The most important choice is the SVN Interface. Subclipse supports two ways of accessing the Subversion server: JavaSVN and JavaHL. The interface is a pure Java Subversion interface, which is reported to be faster than, and to support a few more features than JavaHL. At the time of writing, it did not support file:// protocol. The JavaHL Interface is a native library provided with the Windows installation of Subclipse, and which does support the file:// protocol. However, on a non-Windows platform, you will have to find and compile JavaHL on your own. I guess you can’t have your cake and eat it
Figure : Configuring SubclipseThe first thing you will need to do is set up some repositories in. Open the “SVN Repository” view, and select in the contextual menu (see ). All you need to provide is the Subversion repository URL.
Figure : Adding a new Subversion repositoryThe “SVN Repository” view can be useful for other tasks as well (see ). Using the contextual menu, you can checkout project directories, import local directories into the repository, or export them to your local machine, and even create branches or move directories.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Subversion in NetBeans
- InhaltsvorschauNetBeans provides some excellent Subversion integration features, which we will look at here.Subversion support is not installed in NetBeans 5.5 by default: you need to install it through the Update option. Open the “Tools→Update Center” and make sure the “NetBeans Update Center” option is ticked. Then, in the “NetBeans Update Center” choices, choose “Subversion” (see ). Step though the installation process.When you’re done, a new “Subversion” menu will appear in the main menu bar. Most of the principal Subversion commands are available, either from the main menu, or from the contextual menu for commands relating to a specific file or directory.
Figure : Installing Subversion in NetBeansIn NetBeans 6, both Subversion and CVS are supported out of the box in the “Versioning” menu. The rest of this section will look at the NetBeans 6 Subversion integration.If you want to create a new project by downloading source code from a Subversion repository, NetBeans makes life very easy. Open the “Subversion→Checkout” menu (see ). Here, you provide the URL of the Subversion repository you need to checkout. Once you specify your local directory, NetBeans will proceed to download the entire project into the target directory and set up a corresponding NetBeans project in your .
Figure : Checking out a new project from SubversionOnce the source code has been downloaded, NetBeans will recognize and correctly configure a corresponding NetBeans project for both Ant and Maven-based projects (if you have the Maven extensions installed, that is). If it does not recognize the project structure, you can create a new project based on the new working directory using the normal Project Creation Wizard.NetBeans does a good job of integrating Subversion into the everyday work environment of a NetBeans developer. Update and commit operations can be performed on files or directories using the contextual menu (see ). When you commit your changes, NetBeans will list modified files as well as any new files that you might have forgotten to add manually.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Subversion in Windows
- InhaltsvorschauThere are several Subversion clients for Windows, but one of the better ones is , a free open source tool from Tigris (http://tortoisesvn.tigris.org/). Using TortoiseSVN, Subversion integrates seamlessly into a Windows desktop environment. TortoiseSVN lets you access virtually all the Subversion commands via a graphical client interface, directly from within Windows Explorer (see ). In addition to a slick graphical interface, TortoiseSVN offers nice-to-have features such as spell checking and contextual help in the comments fields.Like Subversion, TortoiseSVN is a very rich product, as can be gleaned by the number of commands available in the contextual menu (see ). Here, we will look at how TortoiseSVN can make life easier for Windows-based Subversion users, concentrating on a few of the most commonly used features.One of the key features of TortoiseSVN is its strong integration with Windows Explorer (see ). You don’t have to run a client application: just open a Windows Explorer window.Virtually all of the Subversion commands are available via the contextual menu, either directly for updates or commits, or in the “TortoiseSVN” submenu for everything else. TortoiseSVN uses intuitive icon overlays to let you see at a glance the status of each file: a green tick indicates an up-to-date file, a red exclamation mark indicates a file (or a directory containing a modified file), a red cross indicates an item scheduled for deletion, and so on.
Figure : TortoiseSVN is a Subversion client that integrates seamlessly with Windows ExplorerIf your project is on a network drive or a removable drive (in fact, pretty much anything other than your standard built-in disk drives), you may not see the icon overlays at first. Open the “TortoiseSVN→Settings” dialog and go to “Look and Feel→Icon Overlays.”If you are working on a remote repository through a proxy server, you will need to configure this, too. You can set proxy settings in the “Network” entry of the “→Settings.”Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Defect Tracking and Change Control
- InhaltsvorschauSubversion, like many other similar tools, is often referred to as an SCM, or Software Configuration Management, tool. Strictly speaking, this is not the case. For people in the Configuration Management (CM) business, the term Software Configuration Management refers to solutions that provide, in addition to version control, features such as defect and change request tracking. These tools should be able to tell you which particular version of code fixed a particular bug (provided that the developers bother to mention this minor detail in their commit messages, of course).Subversion has no built-in issue management solution as such. However, other issue management solutions do integrate well with Subversion. One good example is Trac (see ), a wiki-like issue tracking system closely integrated with Subversion which lets you browse the Subversion repository, keep track of committed modifications, and create links to change sets or individual versions of files from within issues and project documentation.Using Trac, you can browse the Subversion repository (see ), viewing not only the current version but also the revision history associated with each file or directory. You can also review at a glance the differences between different versions of a file. Intuitive hyperlinking makes navigating through the source code repository an easy task.
Figure : Browsing a Subversion repository in TracIssue tracking becomes a particularly powerful tool when you can associate tickets with version control change sets and with particular files in the version control system. For example, when a developer commits her corrections for a particular issue, she can record the corresponding change set and even links the affected files. Later on, when a QA staff member (or another developer) reviews this issue, she can easily visualize all the relevant information, included what version contains the corrected code, what were the developers comments, what files were affected, and even the exact changes made. All this information can be viewed from within the same web interface (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Subversion in Ant
- InhaltsvorschauThere are times when you may want to access your Subversion repository from within your Ant script. For example, this can be useful when writing bootstrap scripts to set up a project environment. The <svn> task, developed by Tigris.org, allows you to do just that. So, without further ado, we will see how to install and use this plug-in in your Ant scripts.The <svn> task is not part of the default Ant distribution: you have to download the latest version from the Tigris.org web site. Download and unzip the latest version. Now you need to declare the <svn> task in your build file, using the <taskdef> tag. For this to work, Ant will need the appropriate JAR files: you can either add the JAR files in the
libdirectory into your Ant lib directory, or refer directly to these jars in the <taskdef> classpath, as shown here:<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask" > <classpath> <fileset dir="${svnant.home}/lib"> <include name="**/*.jar"/> </fileset> </classpath> </taskdef>Note that the <svn> task requires Subversion to be installed on the local machine; it will use either a native JNI library called JavaHL if it can find one, or use the command line directly.The <svn> task lets you do almost anything you could do with Subversion from the command line (with the notable exception of the svn log command, which was not implemented at the time of writing). In SvnAnt, subversion commands are implemented as nested elements of the <svn> task, in a fairly intuitive way. In the rest of this section, we will go though how to perform a few common Subversion tasks with this library.A common requirement is to be able to check out the full source code of a project using a bootstrap script. This can make it easier for users to set up the project, without having to remember the details of where the repository is stored, what branch to check out, and so on. You can checkout a project to a local directory using theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauAs far as open source version control solutions go, Subversion is an excellent choice. It is modern and efficient by design, and some of its features, such as atomic updates, and optimized network traffic, place it technically ahead of many well-known commercial solutions. It integrates well with both Eclipse and NetBeans. Subversion is well documented, particularly for an open source product. Subversion’s main weakness lies in tracking branches and merges, which it does not handle by itself; you need to do this yourself by carefully tracking your merges in the log messages. This can be a downer for big projects and/or undisciplined developers. Nevertheless, it is used by the open source community for many very large projects, and it is arguably one of the best, if not the best, open source version control tool.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 5: Setting Up a Continuous Integration Server with Continuum
- InhaltsvorschauIn this chapter, we look at Continuum (http://maven.apache.org/continuum/). Continuum is a flexible, easy-to-use tool that can help you put Continuous Integration into action. It is fast, lightweight, and undemanding. It is also self-reliant. Like Maven, it is built on the Plexus component framework and comes bundled with its own Jetty application server. For its database needs, it uses Apache Derby, a 100 percent Java fully embedded database. As we will see, this makes Continuum particularly easy to install in almost any environment.Installing a Continuum server is easy. Make sure that you have a recent Java Development Kit (JDK) installed (and JAVA_HOME defined) and download the appropriate installation package from the Continuum web site. I used Java 5 for this chapter.Continuum, like other Continuous Integration tools, uses locally installed tools to check out source code and build your project. So, if you are using Maven and Subversion on your projects, you will need to install these tools on your build machine.Next, just extract the downloaded file into the directory where you want to install Continuum. Extracting the installation package is pretty much all you need to do to get a basic server up and running.If you are installing Continuum onto an integration server, you will probably need to have Continuum starting automatically. Continuum comes with a useful wrapper program and an installation script in the bin\win32 directory called InstallService:
D:\continuum\bin\win32>InstallService wrapper | continuum installed.
This will install a standard Windows service, which you can manage in the usual manner from the Services window (see ). By default, the service will run as the local system account and will be unable to access all of the resources that it needs to start up Continuum. To get around this, you will need to start the service as the same user account in which you installed Continuum. To do this, open the service properties window and change the account used to start the service in the tab “Log On.”Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - An Introduction to Continuum
- InhaltsvorschauIn this chapter, we look at Continuum (http://maven.apache.org/continuum/). Continuum is a flexible, easy-to-use tool that can help you put Continuous Integration into action. It is fast, lightweight, and undemanding. It is also self-reliant. Like Maven, it is built on the Plexus component framework and comes bundled with its own Jetty application server. For its database needs, it uses Apache Derby, a 100 percent Java fully embedded database. As we will see, this makes Continuum particularly easy to install in almost any environment.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing a Continuum Server
- InhaltsvorschauInstalling a Continuum server is easy. Make sure that you have a recent Java Development Kit (JDK) installed (and JAVA_HOME defined) and download the appropriate installation package from the Continuum web site. I used Java 5 for this chapter.Continuum, like other Continuous Integration tools, uses locally installed tools to check out source code and build your project. So, if you are using Maven and Subversion on your projects, you will need to install these tools on your build machine.Next, just extract the downloaded file into the directory where you want to install Continuum. Extracting the installation package is pretty much all you need to do to get a basic server up and running.If you are installing Continuum onto an integration server, you will probably need to have Continuum starting automatically. Continuum comes with a useful wrapper program and an installation script in the bin\win32 directory called InstallService:
D:\continuum\bin\win32>InstallService wrapper | continuum installed.
This will install a standard Windows service, which you can manage in the usual manner from the Services window (see ). By default, the service will run as the local system account and will be unable to access all of the resources that it needs to start up Continuum. To get around this, you will need to start the service as the same user account in which you installed Continuum. To do this, open the service properties window and change the account used to start the service in the tab “Log On.”
Figure : Continuum as a Windows serviceFor Debian-based Linux systems, you can do the following: first, extract Continuum into your/usr/local/directory. Then, for ease of maintenance, create a symbolic link to this directory called continuum:# ln -s continuum-1.1/ continuum # ls -dl continuum* lrwxrwxrwx 1 root root 16 2006-04-29 10:10 continuum -> continuum-1.1/ drwxr-xr-x 9 root root 4096 2006-04-29 10:10 continuum-1.0.3
It just so happens that the Continuum Linux startup script takes the same arguments as the classic Linux boot scripts that we all know and love. So, all you need to do is to update a symbolic link to this script in yourEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Manually Starting and Stopping the Server
- InhaltsvorschauThere are basically two ways to start and stop the Continuum server manually.First, you can start the server using one of the environment-specific start scripts. The basic way to start the server from the command line is as follows:
- For Linux boxes, use ${CONTINUUM_HOME}/bin/Linux/run.sh
- For Macintosh OS X systems, use ${CONTINUUM_HOME}/bin/macosx/run.sh
- On Solaris, use ${CONTINUUM_HOME}/bin/solaris/run.sh
- And on a Windows box, use $CONTINUUM_HOME\bin\win32\run.bat
The basic way to start the server from the command line is to run the start command as follows:# /usr/local/continuum/bin/Linux/run.sh start Starting continuum...Of course, if you have followed the Linux installation procedure described above (see ), you can also do the following:# /etc/init.d/continuum start Starting continuum...You can also restart the server using the (wait for it!) restart command:# /etc/init.d/continuum restart Stopping continuum... Stopped continuum. Starting continuum...And to stop the server, just use the stop command:# /etc/init.d/continuum stop Stopping continuum... Stopped continuum.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking the Status of the Server
- InhaltsvorschauOperating systems being what they are, you may sometimes need to check if Continuum is running correctly. If the process seems to have hung, you can always use the status option to check if there is a process running, and, if so, what its Process Identification (PID) number is. The PID number can always come in handy if you need to kill the process at some point:
$ /usr/local/continuum/bin/Linux/run.sh status continuum is running (16259).This information is actually stored in a file called continuum.pid, in the same directory as the startup script. So, if you accidentally remove this file, the status option can get quite confused.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running the Continuum Server in Verbose Mode
- InhaltsvorschauIf you need to see more details about what the Continuum server is doing (usually to identify and fix a problem), you can always start the server in console mode:
# /etc/init.d/continuum console Running continuum... wrapper | --> Wrapper Started as Console wrapper | Launching a JVM... jvm 1 | Wrapper (Version 3.1.2) http://wrapper.tanukisoftware.org jvm 1 | jvm 1 | [INFO] Services will be deployed in: '../../services'. jvm 1 | [INFO] Applications will be deployed in: '../../apps'. jvm 1 | [INFO] The application server has been initialized. ...
This will display ample details of what the server is getting up to, and hopefully it will also show what is going wrong.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding a Project Group
- InhaltsvorschauOnce the server is running, you can add some new projects to the Continuous Build system. If you’re not logged on, you will need to do this first.In Continuum, projects are organized into groups. The installation comes with a default group (called, rather unimaginatively, “Default Project Group”), but you can create as many of your own groups as you like, depending on your needs. Once you have created a group, you can then proceed to creating projects within this group (see ).
Figure : Adding a Maven 2 projectEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding a Maven Project
- InhaltsvorschauNow that we have a group, let’s add a Maven 2 project. Because of the particularly close integration with Maven 2, this sort of project is easy to add. Continuum simply reads the pom.xml file and gets all the information it needs there (see ). You can either upload a POM file from your local machine, or specify a URL pointing to the POM file on some accessible repository. If you want to follow along at home, you can go pick your favorite Maven-powered open source project, or use the following publicly available URL:
https://wakaleo.devguard.com/svn/jpt-sample-code/maven-schemaspy-plugin/trunk/pom.xml
Figure : Adding a Maven 2 projectNot just any old Maven 2 project will do, however. Your Maven 2 project needs to provide a minimum of useful information to make Continuum happy, so you should check that your project file is correctly configured.In fact, the only really essential information is the Software Configuration Management (SCM) details, which you probably already have. Check out the SCM section of your project’s pom.xml file. You need the connection element to be correctly configured with your local SCM system. Here is an example using Subversion:<scm> <connection> scm:svn:https://wakaleo.devguard.com/svn/maven-plugins /maven-schemaspy-plugin/trunk </connection> <developerConnection> scm:svn:https://wakaleo.devguard.com/svn/maven-plugins /maven-schemaspy-plugin/trunk </developerConnection> <url> https://wakaleo.devguard.com/svn/maven-plugins/maven-schemaspy-plugin/trunk </url> </scm>The syntax used is that of the Maven SCM project (see ” in ). The Maven SCM project (and therefore Continuum) currently support CVS, Subversion, ClearCase, Perforce, Starteam, and Bazaar, although support for other SCM tools such as PVCS, MKS, and Visual SourceSafe is planned.One thing worth knowing is that Continuum expects your pom.xml file to be in the project root directory. Should this not be the case (for example, if you are checking out a from a higher-level directory in your source code repository), you need to provide a relative path to theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding an Ant Project
- InhaltsvorschauIf you want to add an Ant project to your Continuum server, you need to provide a little more detail, simply because this information isn’t present (or at least not in an easy-to-find format) in the build.xml file. Just enter a project name and version (for display purposes), and the relevant SCM details (see ). Note that the SCM URL uses the Maven format for SCM URLs (see ). The SCM URL should point to the project’s root directory.Again, you can follow along at home by using the following URL:
https://wakaleo.devguard.com/svn/jpt-sample-code/jsfpetstore/trunk
Figure : Adding an Ant project to ContinuumBy default, Continuum expects to find a file called build.xml in this directory. If your project is configured differently, you need to edit the build configuration once you have created the project (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding a Shell Project
- InhaltsvorschauYou can even add a project that is built via an script. I once worked on a project where we used a plain old Makefile to read environment-specific variables from a local configuration file, and then call Maven. Other examples could be when initialization or cleanup tasks need to be done before or after the main build task.The script must respect a few conditions: it should call Maven at some point, and it should return a result value of 0 for success and 1 for failure.Setting up a script in Continuum is similar to setting up an Ant project (see ). You just provide a name and version and appropriate SCM information.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Managing Your Project Builds
- InhaltsvorschauContinuum provides a convenient dashboard where you can see the status of all your projects at a glance (see ). Projects are organized in groups, which is convenient because, in practice, you will often have several build projects for a given development project.Each project is listed with an icon indicating the current build status (1). See the following table.

Success—The last project build was an unqualified success. 
Failure—Uh-oh. There is a problem of some kind, such as a compilation error or unit test failures. The build history screen will help isolate the exact problem. 
Error—A major stuff-up has occurred. This is often a configuration error or a problem with obtaining the source code from the version control system. Checkout the build history for more details. In the Build column (2), you will find the number of attempted integration builds that have taken place since the project was added to Continuum. Special icons will sometimes appear here if a build is queued or in progress.
Figure : The Continuum project dashboardFurther to the right (3), we have a set of useful little icons, which allow you (going from left to right) to force an immediate build (useful to verify whether the correction you just committed has fixed the integration problem detected by Continuum), to display the build history, and to display the working copy used by Continuum for its builds.If you click on the project name, you can view and modify the build configuration details (see ). This is a useful screen where you can configure and schedule build targets, set up notifications, and (for Maven projects) display project dependencies and team details. We will look at this page in more detail later on.The Build History page lets you consult a list of all builds for this project (see ).
Figure : The Continuum Build Configuration details
Figure : The Continuum Build History pageYou can also display detailed results for each build, including changes made to the source code repository and the appropriate build logs (see ). This isEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing Users
- InhaltsvorschauContinuum provides good support for user management. As you might expect, you add or delete users as required in the “Users” screen (see ). This screen also lets you define user roles for each user, either in terms of global roles that apply across all projects (such as “System Administrator” or “Guest”), or, more specifically, for individual projects. For specific projects, you can define a user as being a Project Administrator (who can do pretty much anything within that particular project), a Project Developer (who can start builds), or a Project User (who can consult the build results).
Figure : A Continuum build reportEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Notifiers
- InhaltsvorschauReceiving notification by mail is all very well, but sometimes mail is slow to arrive, or it can be ignored. Continuum provides for a few other notification mechanisms, such as various forms of chat and instant messaging. At the time of this writing, these current mechanisms were supported:
- IRC
- Jabber
- MSN
All of these are fairly easy to configure from the Continuum web console. When you configure instant messaging, you will need to set up a special account with your messaging service for the Continuum server. Most instant messaging services don’t appreciate users being logged on simultaneously in several different places, so you can’t use an account that is already being used by someone in the development team.The main problem with Continuum notifications is that you need to set them up one-by-one for each project team member, even if the members are defined in the POM file.There is no out-of-the-box support for RSS, although this is not hard to implement using the Continuum RCP Application Programming Interface (API).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring and Scheduling Builds
- InhaltsvorschauAs far as builds are concerned, the term “Continuous Integration” is inexact: “Regular Automatic Integration” would be closer, but it probably doesn’t sound as snappy. In any case, builds on a Continuous Integration server need to be scheduled carefully: too few, too far apart, and you lose in reaction time; too many, too close together, and your server may go down. And if you have several projects on the same server, it’s advisable to schedule them in a way that avoids all the projects being built at the same time.As you would expect, Continuum lets you set up build schedules for your projects (see ). You can define as many build schedules as you like, giving them (hopefully) meaningful names and planning the builds using the superbly powerful, if somewhat cryptic, Cron expression syntax. A detailed description of the Cron syntax can be readily found on the Internet, but we’ll give a brief description here for clarity.Cron expressions are a precise, concise, and flexible way of describing exactly when a task should be performed. In Continuum, a Cron expression is composed of seven fields, which represent seconds, minutes, hours, days of the month, months, days of the week, and years (see Table 5-2).
Table : CRON fields in Continuum Field Authorized values Seconds 0-59 Minutes 0-59 Hours 0-23 Days of the month 1-31 Months 1-12 or JAN-DEC Days of the week 1-7 or SUN-SAT Years (optional) 1970-2099 There are also some important special characters:- The “*” character is a wildcard—it can be used in any field, to represent any authorized value in that field.
- The “-” character expresses ranges: “9–5” in the hours field means “All the hours from 9 to 5.”
- The “,” character expresses a sequence of values; for example, “MON,WED,FRI” means “Mondays, Wednesdays, and Fridays.”
- The “/” character expresses increments; for example, in the minutes field, “0/5” means every 5 minutes, and “15/5” means every 5 minutes from a quarter past on.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Debugging Your Builds
- InhaltsvorschauOnce you set up your build, you need to ensure that it runs correctly. Otherwise, you may find yourself overwhelmed by a flood of abusive email from irate developers who have been told (incorrectly, for once) that their build is broken.When you add a build, you can either wait for it to run automatically or, better still, trigger a build by hand to make sure it works. If it fails, you can check out exactly what happened in the “Result” screen, which you can access through the link of the same name in the Builds tab. This screen shows the raw output of your build execution, which is often enough to isolate and correct the problem.Another useful tool is the Working Copy tab. Here you can view the directory structure and files that Continuum used for the build. You can compare this with your own local copy to make sure all the necessary files are there, and that their content is correct.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Configuring the Continuum Mail Server
- InhaltsvorschauThe Continuum web site provides a good summary of the project build status, but developers cannot be expected to stay glued to the screens watching it—they have to write code from time to time, too! As we have seen (see ), that’s what notifiers are for.By default, the mail notifier will attempt to use the mail service installed on the machine where Continuum has been installed. The default configuration expects to find a Simple Mail Transfer Protocol (SMTP) server on the local machine (local host) using the standard SMTP port (25): this will work fine for most Linux or Unix environments with a mail service installed.However, you may need to configure the mail server for Continuum if you are not sending mail directly via a service on the server. To do this, you need to modify the application configuration file, which can be found at apps/continuum/conf/application.xml. This is the Plexus framework configuration file for your Continuum server, and as such should be treated with due caution and respect. The bit you need to look for is the
MailSendercomponent: modify the values to suit your environment. Here is one possible configuration:<component> <role>org.codehaus.plexus.mailsender.MailSender</role> <implementation>org.codehaus.plexus.mailsender.javamail.JavamailMailSender </implementation> <configuration> <smtp-host>smtp.mycompany.com</smtp-host> <smtp-port>25</smtp-port> <sslProvider>com.sun.net.ssl.internal.ssl.Provider</sslProvider> <username>mylogin</username> <password>mypassword</password> <sslMode>true</sslMode> </configuration> </component>It’s worth taking some time to think about how build results are going to be published. As a rule, for example, notifications for successful builds are of limited value to anyone except (possibly) the developer who just committed some changes. Failures, warnings, and errors, by contrast, should definitely be required reading for the whole team. Using a mailing list for noncritical messages is often a good compromise.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring the Continuum Web Site Ports
- InhaltsvorschauBy default, Continuum runs its web server on port 8080, and listens for XML-RPC requests on port 8000. Of course, this may not suit everyone. To change these ports, you need to modify the following configuration file:
apps/continuum/conf/application.xml
Edit this file and find the services section. In the following listing, the port is set to 8081 instead of the default 8080:<services> <service> <id>jetty</id> <configuration> <webapps> <webapp> <file>${plexus.home}/lib/continuum-web-1.0.3.jar</file> <context>/continuum</context> <extraction-path>${plexus.home}/webapp</extraction-path> <listeners> <http-listener> <port>8081</port> </http-listener> </listeners> </webapp> </webapps> </configuration> </service> ...Now just restart the Continuum server.As of Continum 1.1, you can also configure the port in theconf/plexus.xmlfile, as shown here:<plexus> ... <!-- START SNIPPET: jetty_port --> <component> <role>org.codehaus.plexus.contextualizer.Contextualizer</role> <role-hint>jettyConfiguration</role-hint> <implementation>org.codehaus.plexus.contextualizer.DefaultContextualizer </implementation> <configuration> <contextValues> <jetty.port>8081</jetty.port> </contextValues> </configuration> </component> ... </plexus>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Automatically Generating a Maven Site with Continuum
- InhaltsvorschauOne of the nice features of Maven is its built-in ability to generate a web site containing an abundance of technical information and reports about your project (see ). One common use of Continuum is to automatically generate and publish this web site.The standard way to build a Maven web site is to run the following command:
$ mvn site
This will generate a Maven site in the target directory. However, it is of limited use there. You generally want to copy it to a local web server, in order to publish your site to the world (or even just for your fellow workers). One simple (some would say “quick and dirty”) way to do this is to use the stagingDirectory variable. The following code will generate a Maven site and publish it in the /var/www directory:$ mvn site -DstagingDirectory=/var/www
This will deploy your Maven web site to the /var/www directory, in a directory with the same name as your project.Typically, you run CI builds regularly. If the build fails, the build indicator goes red, notifications are sent, and the build stops. This is fine for detecting build failures, but is less satisfactory if you need to publish a web site to display, among other things, details of these build failures.The conventional way to do this is to use two distinct build tasks: one to build the application and run the unit and integration tests, and another to publish the web site. The first should fail if there are any test failures, the second should not. To prevent a Maven target from stopping in the presence of unit test failures, use the maven.test.testFailureIgnore command- option, as shown here:$ mvn site -DstagingDirectory=/var/www -Dmaven.test.testFailureIgnore=true
This is illustrated in , in which the Maven site is updated every hour, even if there are unit test failures.
Figure : Building the Maven site with hourly buildsAnother approach, originally suggested by Max Cooper, is to set up a profile in which test failures are ignored.<!-- skip tests by default, but allow override on command line --> <profile> <id>skiptests</id> <activation> <property> <name>!maven.test.skip</name> </property> </activation> <properties> <maven.test.skip>true</maven.test.skip> </properties> </profile>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring a Manual Build Task
- InhaltsvorschauContinuum is without doubt a useful tool for optimizing the build process. Developers will appreciate getting quick feedback on any new integration issues thanks to regular automatic builds. However, in real-world projects, not all builds are automatic. For example, in a test or staging environment, you generally want to decide yourself when a new version is to be released onto the test environment, rather than doing it automatically. Test releases need more coordination and communication efforts than regular automatic builds for developers: test teams appreciate being able to test on a stable, well-defined version, and not be confronted suddenly by the latest snapshot.As we have seen, Continuum makes it easy to set up one or several automatic builds. You can also use Continuum to make your life easier when you build the release versions of your product. This way, releases can be done directly from the Continuum web interface, in a controlled environment, rather than on a developer’s machine.To do this, you need to set up a schedule that is never actually run. We will use this special schedule to define tasks that are to be activated manually.Create a new schedule, with a name like “MANUAL_TASK.” You will need to provide a syntactically correct CRON expression, although you may want to give an impossible value such as a past date (e.g., “0 0 12 * * ? 1999” ) or one far in the future (e.g., “0 0 12 * * ? 2099” ) to make your intention clear. As we will see, this value will not actually be used. Just make sure that the schedule is disabled (uncheck the “Enabled” checkbox at the bottom of the screen) and save.Now you need to create an additional build target for your manual build. This can be a separate project or an additional build target within the same project. In both cases, you will end up with a build definition like the second one in .
Figure : A project containing manual build definitionsNext, you need to distinguish the target environments, so that your manual task deploys to your staging environment. In Ant, you might do this using command-line property definitions or by calling a different target, for example. The above example is for a Maven 2 project. Maven profiles (see ” in ) are a good way to customize environment-specific deployment details. You can set up profiles for each target environment in yourEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauContinuum is still a relatively young product, with a lot of room for new and improved features. It has, nevertheless, made a lot of progress since it first appeared. It is easy to install and easy to use and will suffice in many situations. Its simplicity and its intuitive web administration console make it a good candidate for introducing continuous integration into organizations that have had no previous experience with the subject.On the negative side, Continuum lacks some of the more advanced features found in other tools, although this has greatly improved since the release of Continuum 1.1.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 6: Setting Up a Continuous Integration Server with CruiseControl
- InhaltsvorschauCruiseControl is a widely used and widely regarded open source continuous integration tool written in Java. CruiseControl is backed by ThoughtWorks, a leading proponent of agile methodologies and open source technologies. One of the first open source Continuous Integration tools, it benefits from a large user base and a number of third-party tools. ThoughtWorks now also offers CruiseControl Enterprise, a supported version of the product.For those who skipped the introduction to this part of the book, Continuous Integration is a powerful technique used to keep development teams in phase and reduce the risks and complexities involved in combining code written by a number of individual developers into a unified working product. It basically involves automatically building and testing the latest source code at frequent intervals. At the same time, team members test and commit their changes frequently into the source code repository. It can considerably reduce the costs and risks traditionally involved in the integration phase of a project.CruiseControl falls into the same category of tools as Continuum (), LuntBuild (), and Hudson (). CruiseControl is the oldest of the three tools. Up until recently, configuration was still entirely done via an Extensible Markup Language (XML) configuration file. As of version 2.7, a basic administration console was added, similar to the ones found in the other tools we look at. CruiseControl supports an extensive range of software configuration management tools and notification mechanisms.In this chapter, we will talk about CruiseControl 2.7.1, the latest release at the time of this writing.CruiseControl runs as a process on the Continuous Integration server (known as the “Build Loop”) that periodically checks for updates in the source code repository. Whenever updates are detected in a project’s source code, CruiseControl runs the build process and notifies a configurable list of listener modules. These listener modules let you notify team members about build results via email, IM, RSS, or using other methods.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- An Introduction to CruiseControl
- InhaltsvorschauCruiseControl is a widely used and widely regarded open source continuous integration tool written in Java. CruiseControl is backed by ThoughtWorks, a leading proponent of agile methodologies and open source technologies. One of the first open source Continuous Integration tools, it benefits from a large user base and a number of third-party tools. ThoughtWorks now also offers CruiseControl Enterprise, a supported version of the product.For those who skipped the introduction to this part of the book, Continuous Integration is a powerful technique used to keep development teams in phase and reduce the risks and complexities involved in combining code written by a number of individual developers into a unified working product. It basically involves automatically building and testing the latest source code at frequent intervals. At the same time, team members test and commit their changes frequently into the source code repository. It can considerably reduce the costs and risks traditionally involved in the integration phase of a project.CruiseControl falls into the same category of tools as Continuum (), LuntBuild (), and Hudson (). CruiseControl is the oldest of the three tools. Up until recently, configuration was still entirely done via an Extensible Markup Language (XML) configuration file. As of version 2.7, a basic administration console was added, similar to the ones found in the other tools we look at. CruiseControl supports an extensive range of software configuration management tools and notification mechanisms.In this chapter, we will talk about CruiseControl 2.7.1, the latest release at the time of this writing.CruiseControl runs as a process on the Continuous Integration server (known as the “Build Loop”) that periodically checks for updates in the source code repository. Whenever updates are detected in a project’s source code, CruiseControl runs the build process and notifies a configurable list of listener modules. These listener modules let you notify team members about build results via email, IM, RSS, or using other methods.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing CruiseControl
- InhaltsvorschauThis section will show you how to get CruiseControl up and running on your machine.I recommend creating a dedicated user account for CruiseControl. On a Linux machine, you can create a new user, as follows:
# useradd cruisecontrol # su - cruisecontrol cruisecontrol@linux-gxu0:~>
Now download the CruiseControl binaries from the CruiseControl web site (http://cruisecontrol.sourceforge.net/download.html). CruiseControl comes packaged as either a Windows executable installer or as a simple ZIP file. Here we will use the ZIP file, as this will work in any environment. If you are installing CruiseControl on a remote server, you can download the latest version from the CruiseControl web site using a command-line tool like wget, as shown here:$ wget http://prdownloads.sourceforge.net/cruisecontrol/cruisecontrol -bin-2.7.1.zip?download ... 13:46:56 (30.21 KB/s) - 'cruisecontrol-bin-2.7.1.zip?download' saved [14487]
Now unzip the package into the home directory:$ unzip cruisecontrol-bin-2.7.1.zip Archive: ../cruisecontrol-bin-2.7.1.zip creating: cruisecontrol-bin-2.7.1/ creating: cruisecontrol-bin-2.7.1/lib/ creating: cruisecontrol-bin-2.7.1/logs/ creating: cruisecontrol-bin-2.7.1/logs/connectfour/ inflating: cruisecontrol-bin-2.7.1/cruisecontrol.bat inflating: cruisecontrol-bin-2.7.1/lib/commons-el.jar inflating: cruisecontrol-bin-2.7.1/lib/commons-logging.jar inflating: cruisecontrol-bin-2.7.1/lib/jasper-compiler.jar ... $ cd cruisecontrol-bin-2.7.1 $ ls apache-ant-1.7.0 cruisecontrol.bat dashboard-config.xml lib projects webapps config.xml cruisecontrol.sh docs logs README.txt widgets.cfg
And that’s it as far as installing things goes. Now we can test the installation. CruiseControl comes with its own bundled Jetty web server, so you don’t have to worry about configuring (yet) another web server to monitor builds. Starting up CruiseControl is simple: just run thecruisecontrol.shscript (orcruisecontrol.batfor Windows ):$ cd ~/cruisecontrol-bin-2.7.1 $ ./cruisecontrol.sh [cc]Sep-19 01:08:29 Main - CruiseControl Version 2.7.1 Compiled on September 4 2007 1821 ... [cc]Jun-18 14:09:38 ontrollerAgent- Starting HttpAdaptor with CC-Stylesheets [cc]Jun-18 14:09:38 ontrollerAgent- starting httpAdaptor [cc]Jun-18 14:09:38 BuildQueue - BuildQueue started HttpAdaptor version 3.0.1 started on port 8000 [cc]Jun-18 14:09:38 Container - Started org.mortbay.jetty.servlet. WebApplicationHandler@e2cb55 [cc]Jun-18 14:09:38 Container - Started WebApplicationContext [/,CruiseControl Reporting App] [cc]Jun-18 14:09:39 Container - Started org.mortbay.jetty.servlet. WebApplicationHandler@29e357 [cc]Jun-18 14:09:39 Container - Started WebApplicationContext[/cruisecontrol, CruiseControl Reporting App] [cc]Jun-18 14:09:39 SocketListener- Started SocketListener on 0.0.0.0:8080 [cc]Jun-18 14:09:39 Container - Started org.mortbay.jetty.Server@b4d3d5Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring an Ant Project
- InhaltsvorschauNow that the server is installed, we’ll look at how to add a simple project to CruiseControl. CruiseControl works best with Ant, so we’ll look at this configuration first.When it builds a project, CruiseControl uses a dedicated work directory for each project, where it checks out the source code and runs its builds. However, Cruise will not create this directory by itself, even if you tell it where to find the SCM tool. You have to do it yourself. So the first thing to do is to create a working directory for your project. These directories live (by convention) in the $CC_HOME/projects directory, where $CC_HOME is your CruiseControl installation directory. So before you start, you need to manually create the project and check out an initial copy of the source code. In this example, we will be working with a sample project called library-loans, an imaginary API designed to interface to a library loans database. First, we check out our project from the SCM repository into the projects directory (in this example, we use Subversion [], but CruiseControl supports a wide range of SCM tools):
$ su - cruisecontrol Password: $ cd cruisecontrol-bin-2.7.1/projects/ $ svn co svn://localhost/library-loans/trunk library-loans A library-loans/test ... A library-loans/build.xml Checked out revision 31. $ ls connectfour/ library-loans/
It’s also wise to verify that the build works properly in this environment. A lot of time-wasting errors come from badly configured Continuous Integration user accounts. In this sample project, we use some fairly standard ant targets: clean, to delete all generated artifacts; compile, to compile the main classes; build, to package the compiled classes into a jar called library-loans.jar; and test, to run the unit tests against this jar. Our continuous build process will go as far as running the unit tests:$ cd library-loads $ ant test Buildfile: build.xml init: ... [junit] Testcase: testSetName took 0.004 sec [junit] Testcase: testSetSymbol took 0 sec BUILD SUCCESSFUL Total time: 2 secondsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Keeping People Notified with Publishers
- InhaltsvorschauIt’s a good thing to have your builds running regularly, but it’s even better to let one know how they’re going. Developers will not, as a rule, spend their time glued to the CruiseControl web console waiting for the next build results to come up: they have better things to do with their time.Publishing notification is one of the areas in which CruiseControl excels. Out-of-the-box, CruiseControl supports eMail, Jabber IM, RSS feeds, and even blogs. You can also perform various tasks such as ftp or scp file transfers, invoking an Ant target, running command-line tasks, or even using an X10 interface to hook up a lava lamp.
Figure : The CruiseControl web site displays metrics concerning build history for a projectPublishers are usually called after every build, whether the build succeeds or fails. If you need to run a publisher task only when a build succeeds or only when it fails, you can place it inside either a <onsuccess> or <onfailure> block, respectively. So, in the following example, the JAR file is deployed to the artifacts directory only if the build is successful:<publishers> <onsuccess> <artifactspublisher dest="artifacts/${project.name}" file="${build}/${project.name}.jar"/> </onsuccess> </publishers>The most common notification method is by email. CruiseControl supports both plain text email and HyperText Markup Language (HTML) email. The plain text email (<email>) simply sends a lightweight text message containing a link to the build results (see ). Configuring the email publisher is relatively straightforward. A typical example is shown here:<property name="web.server.url" value="http://localhost:8080"/> ... <publishers> .... <email mailhost="localhost" returnaddress="cruisecontrol" defaultsuffix="mycompany.com" subjectprefix="Build report:" reportsuccess="fixes" spamwhilebroken="false" buildresultsurl="${web.server.url}/buildresults/${project.name}"> <always address="build-archives" /> <failure address="developers" reportWhenFixed="true"/ /> <map alias="john" address="john.smart@mycompany.com" /> <map alias="mike" address="mikel@othercompany.com" /> <map alias="developers" address="john, mike, harry" /> </email> .... </publishers>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a Maven 2 Project in CruiseControl
- InhaltsvorschauCruiseControl isn’t as well integrated with Maven as it is with Ant. However, you can actually use the SCM features of Maven to make life easier. In this section, we’ll go through the steps involved in configuring a Maven 2 project under CruiseControl.For this example, we’ll use a project called “tasker.”First of all, check out the source code in a working directory for CruiseControl:
$ cd projects $ svn co svn://localhost/tasker/trunk tasker A tasker/src ... Checked out revision 24. $ ls connectfour library-loans tasker
Now add the Maven project to the CruiseControl configuration file.Next you need to set up version control. In CruiseControl, this is easier to do for a Maven 2 project than for an Ant project, mainly because of Maven’s integrated lifecycle support. In Maven 2, you specify the source code repository using the <scm> tag in your pom.xml file. A simple <scm> entry might look like this:<scm> <connection>scm:svn:svn://subversion.mycompany.com/tasker/trunk</connection> <developerConnection>scm:svn:svn://subversion.mycompany.com/tasker/trunk </developerConnection> </scm>If this is correctly set up, you can update your project using the Maven scm plugin, as shown here:$ mvn svn:update
In other words, you don’t have to worry about the build wrapper we saw in the Ant project configuration. You just have to add the project to the CruiseControl configuration file. Here is an example:<project name="tasker"> <listeners> <currentbuildstatuslistener file="logs/${project.name}/status.txt"/> </listeners> <modificationset quietperiod="0"> <svn localWorkingCopy="projects/${project.name}"/> </modificationset> <schedule interval="10"> <maven2 mvnscript="/usr/local/bin/mvn" pomfile="projects/${project.name}/pom.xml" goal="scm:update package" /> </schedule> <log> <merge dir="projects/${project.name}/target/surefire-reports"/> </log> <publishers> <onsuccess> <artifactspublisher dest="artifacts/${project.name}" file="projects/${project.name}/target/${project.name}-1.0.jar"/> </onsuccess> </publishers> </project>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The CruiseControl Dashboard
- InhaltsvorschauFor a long time, the CruiseControl user interface was somewhat basic, to say the least. In the more recent releases, however, there is a new and revamped graphical dashboard that gives you a convenient overview of the status of your projects, as shown in . This dashboard gives you a color-coded summary of the status of your builds: tones of green for success, tones of red for failure, yellow for a build in progress, and gray for inactive projects.
Figure : The CruiseControl dashboardYou can also drill down to view the details of a particular build, or view a summary of all the latest build results in the Builds tab. From here, you can also force a build to run manually. The dashboard also lets you add a project to the CruiseControl configuration file (although in general you still need to go and tailor the configuration file manually to suite your needs), and provides access to RSS feeds for server build results.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Third-Party Tools
- InhaltsvorschauCruiseControl boasts a rich collection of third-party tools. In this section, I describe a few of the more useful of them. Many, if not most, are listed on the CruiseControl wiki.The CruiseControl Configuration UI is a Java client application that can help you write and maintain CruiseControl configuration files using a Swing interface (see ). The CruiseControl configuration file is presented in the form of a tree, in which you can add, delete, or modify elements. One nice piece of functionality is the help panel, which displays contextual help for the current element and can be a useful learning aid or memory-jogger.
Figure : The CruiseControl Configuration UI in actionDmitri Maximovich has written a neat little CruiseControl plug-in for Firefox and Thunderbird. You configure the plug-in by specifying a name and the JMX URL for the server (or servers) you want to monitor. CruiseControl runs JMX by default on port 8000, so your JMX URL will probably look like http://cruisecontrol.mycompany.com:8000. Once you’ve configured your server, you will see a panel in the lower right corner of the Firefox window, which summarizes the current build status (see ).
Figure : A CruiseControl plug-in for FirefoxEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauCruiseControl is a powerful and flexible continuous integration tool, albeit with a nontrivial learning curve. It is a pure CI tool, with little in the way of interproject or build artifact management. However, its flexibility and sophisticated notification techniques can make it an excellent choice for experienced Continuous Integration practitioners.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 7: LuntBuild—A Web-Based Continuous Integration Server
- InhaltsvorschauLuntBuild is another open source Continuous Integration tool written in Java. It is quite easy to install and configure, and all server administration tasks are done via a simple but convenient web-based administration console. In fact, LuntBuild is designed to do more than just managing the continuous integration process: it also lets you store and manage generated artifacts, label and promote versions, and manage dependencies between builds. It supports a wide range of version control tools, and notifications can be diffused via email, instant message (IM), and even on a blog site. Indeed, it is one of the most feature-rich of the open source Continuous Integration tools.LuntBuild is produced and maintained by a company called PMEase. The source code is hosted on JavaForge. LuntBuild also has a commercial cousin, called QuickBuild, which is marketed by the same company. Quickbuild is a commercial open source product (the source code is provided at a cost), with some extra features such as enhanced user and group management, and functionality aimed at larger organizations managing a large number of projects.Installing and configuring LuntBuild is relatively simple, and you can have a server up and running in less than an hour. The installation package comes in the form of a self-installing jar file. Download it from the LuntBuild web site and run the graphical installation program as follows:
$ java -jar LuntBuild-1.5.2-installer.jar
The installer takes you step-by-step through the installation process. LuntBuild is a very flexible tool, and can be configured to work with many external web servers and databases. It also works just fine all by itself as a standalone application. During the installation process, you can choose among various configuration options. If you leave the fields with their default values, LuntBuild will install and configure a fully functional standalone installation. For a production environment, you may want to modify some of the options, such as installing it on your own web application server or using your enterprise database. Some of the choices are described briefly here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - An Introduction to LuntBuild
- InhaltsvorschauLuntBuild is another open source Continuous Integration tool written in Java. It is quite easy to install and configure, and all server administration tasks are done via a simple but convenient web-based administration console. In fact, LuntBuild is designed to do more than just managing the continuous integration process: it also lets you store and manage generated artifacts, label and promote versions, and manage dependencies between builds. It supports a wide range of version control tools, and notifications can be diffused via email, instant message (IM), and even on a blog site. Indeed, it is one of the most feature-rich of the open source Continuous Integration tools.LuntBuild is produced and maintained by a company called PMEase. The source code is hosted on JavaForge. LuntBuild also has a commercial cousin, called QuickBuild, which is marketed by the same company. Quickbuild is a commercial open source product (the source code is provided at a cost), with some extra features such as enhanced user and group management, and functionality aimed at larger organizations managing a large number of projects.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing LuntBuild
- InhaltsvorschauInstalling and configuring LuntBuild is relatively simple, and you can have a server up and running in less than an hour. The installation package comes in the form of a self-installing jar file. Download it from the LuntBuild web site and run the graphical installation program as follows:
$ java -jar LuntBuild-1.5.2-installer.jar
The installer takes you step-by-step through the installation process. LuntBuild is a very flexible tool, and can be configured to work with many external web servers and databases. It also works just fine all by itself as a standalone application. During the installation process, you can choose among various configuration options. If you leave the fields with their default values, LuntBuild will install and configure a fully functional standalone installation. For a production environment, you may want to modify some of the options, such as installing it on your own web application server or using your enterprise database. Some of the choices are described briefly here:- Customizing the web server
- LuntBuild is a web-based application. As such, you can either deploy it on your favorite Java web server (such as Apache Tomcat), or let LuntBuild run its own standalone web Jetty-based web server. If you want to deploy it onto your own J2EE-compliant application server, just specify the war deployment directory, and LuntBuild will do the rest.
- Configuring the database
- LuntBuild uses a relational database to keep track of generated artifacts, build history, and so on. By default, LuntBuild uses an embedded HSQL DB database, but you can also configure it to use an external database such as MySQL, , Oracle, or Microsoft SQL Server.
- Configuring authentication
- If your company has an LDAP-based user directory, you can configure LuntBuild to use it for logins and passwords. LuntBuild supports some quite sophisticated options for LDAP integration: LuntBuild can import your LDAP users into the LuntBuild user directory where you can give them LuntBuild-specific access rights, you can give all the LDAP users read-only access to the site, you can reserve project administration rights to a selected few, you can map LuntBuild user IDs and email addresses to LDAP fields, and so on.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring the LuntBuild Server
- InhaltsvorschauOnce you have started the server, you manage everything via the web administration console (the URL will be something like http://localhost:8080/LuntBuild). Logically enough, LuntBuild requires you to identify yourself before you can do anything interesting (see ). When you first install LuntBuild, you can use “LuntBuild/LuntBuild” to connect as an administrator. Anonymous users are also allowed to view certain parts of the site, but cannot modify anything.The LuntBuild administration web site lets you view and manage different aspects of your server and of the projects you manage (see ). Here, you can manage build projects and build history, user accounts, server configuration properties, and perform other administrative tasks such as backing up and restoring build and artifact history.
Figure : Connecting to the LuntBuild administration consoleThe first thing you need to do when you set up your server is to verify the server configuration properties. You do this in the “Properties” tab (see ). Many server configuration properties, such as the server URL and work directories, use sensible default values, which are well documented on the Properties page. Others, such as the SMTP server for mail notification, will need to be configured if you want to use these notification methods. LuntBuild supports a fairly limited range of notification methods, including SMTP email, MSN, Jabber, SameTime, and some blog sites. To use any one of these methods, you will need to provide information on the host server, as well as user account and password details.
Figure : The LuntBuild home pageIf configuring one of the IM notification types (MSN or Jabber), you will need to create a special account for the LuntBuild server (e.g., LuntBuild.mycompany@hotmail.com). Users will need to add this account to their list of contacts in their IM clients. Then users simply log on to their IM clients as usual.To use blog notification, you will need to provide user account information, the URL of the blog web service, and also the type of blog. Several blog sites today let you submit posts via a web service interface. Unfortunately, there is no standard interface. At the time of this writing, LuntBuild supports the three main blogging application programming interfaces (API’s): the Blogger API (the oldest standard, used for sites like ), the Metaweblog API (an extension of the Blogger API, used for sites like and ), and the LiveJournal API (used by the blog site).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding a Project
- InhaltsvorschauIn LuntBuild, build schedules are organized around projects. LuntBuild provides a rich set of functions for managing project builds.You add a new project in the “Project” tab (see ) by clicking on the “New Project” icon. LuntBuild lets you configure a number of different aspects of your project (see ). Don’t let the number of screens put you off, though; the screens are intuitive and well documented, and you can get a simple project up and running in very little time.In LuntBuild, project information and management activities are organized into five areas, each represented by a tab:
- Basic
- This is where you define general project details, user access rights, and notification methods.
- VCS Adaptors
- Version control systems (also known as SCM, or Software Configuration Management, systems).
- Builders
- The Builders page lets you configure one or more build scripts for this project.
- Schedulers
- Schedulers let you specify when the build scripts will be run.
- Login mapping
- This page lets you map your LuntBuild users to your VCS users.
Figure : The Projects tabIn the remainder of this section, we will go through the steps involved in configuring a project in LuntBuild.The first thing to define are the general project details, user access rights, and notification methods, which are displayed in the “Basic” tab (see ). You will need to save this information before LuntBuild will let you proceed to the other screens.An important part of this screen concerns user rights. In LuntBuild, you can assign different levels of access to different users for each project. There are three types of access:- Project Admins
- Project admins can manage all aspects of projects in LuntBuild, including project and SCM configuration, user management, and build management.
- Project builders
- Project builders are restricted to build scheduling and build management activities.
- Project viewers
- Project viewers can simply consult build results and download generated artifacts.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Project Variables for Version Numbering
- InhaltsvorschauManaging version numbers is an important part of configuration management. LuntBuild provides several easy ways to do this, via the “Next build version” field in the “Schedules” tab.If all you need is a basic counter, you simply provide the initial version text in the “Next build version” field. For example, if you place “my-project-1.0” in the “Next build version” field, the next build will be labeled “my-project-1.0,” the following “my-project-1.1,” and so on. This method simply takes the last number in the version label field and increments it, so “my-project-1.5.0_07-b10” would become “my-project-1.5.0_07-b11.”Now suppose you use a numbering system based on major and minor release numbers, followed by a build number; for example, “my-project-1.2.3.” You can do this using project variables, which you define in the “Basic” tab (see ), and OGNL expressions.OGNL, or Object-Graph Navigation Language, is an expression language for manipulating Java objects. It is often used in the open source Java world, in projects such as Tapestry and Webworks. It is similar but more expressive than the more well-known EL expression language used in JSTL, which only allows you to read object properties.In LuntBuild, project variables are stored in a Map called project.var. So, to read a project variable called “versionNumber,” you would use the following expression:
${project.var["versionNumber"]}To define the “my-project-1.2.3” numbering strategy described above, you would define two variables in the project “Basic” tab called “majorVersionNumber” and “,” and use a version label like this:myproject-${project.var["majorVersionNumber"]}-${project.var["minorVersionNumber"]}-1However, this won’t work. When you introduce OGNL, the trick of incrementing the last number in the build label no longer works—you need to do this yourself. That’s where OGNL expressions come in handy. Not only can you read object fields, but you can also modify them. Here, we need a third variable (say, “versionIterator”) to keep track of the final build number. You use theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Build Results Diagnostics
- InhaltsvorschauOn the home page of your LuntBuild site, you get a dashboard summary of the latest builds, with green lights for successful builds, and red lights for failed ones (see ). What can be a little confusing is the fact that both schedules and builds have status lights: Both can fail, apparently independently of each other.If the schedule is marked with a red light (like the “Java Power Tools” schedule in ), the schedule is not (or is no longer) running correctly. The build, if any, is the last successful build on record.If the build is marked with a red light (like the “alexandria-1.0” build in ), this tells you that your schedule may be running just fine, but your build script (or the configuration thereof) is broken.
Figure : Displaying build resultsIn both cases, something probably needs to be fixed. Just click on the broken schedule or build to investigate further.Finding out exactly what is wrong can be a little tricky, and sometimes needs some detective work. Let’s look at the broken build first. If you want to see details about what is broken, just click on the broken build. This will open a page containing a detailed description of the build results (see ). You can do a few things here, such as displaying logfiles, and forcing a rebuild when you think you’ve fixed the problem (the “hammer” icon).You can also attach this build script to a different scheduler using the green arrow icon (the operation is called “move” in LuntBuild jargon). This could be useful for example if you realise that the build you have scheduled to run every 10 minutes actually takes 30 minutes, to complete, which is an excellent way to overload your build server and to incur the wrath of your system administrator.
Figure : Build result detailsThe revision log contains the list of changes recorded in the VCS since the last build. The build log (see ) gives a detailed record of the build’s progress. Even if no error is explicitly mentioned here, you can generally figure out where the build got to. For example, in this case, we notice that the build stopped with the cryptic message “Seems that baseUrl ‘svn://localhost/Alexandria/tags’ does not exist, creating… tries to create the Subversion tags directory, and stops there. This seems to indicate a problem with the Subversion connection.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using LuntBuild with Eclipse
- InhaltsvorschauLuntBuild integrates well with the Eclipse IDE, via the Luntclipse plug-in. This plug-in is a neat little tool that gives you a full view of the status of your LuntBuild projects without having to leave your IDE or open a browser. Let’s look at how to install and use the Luntclipse plug-in.
Figure : Setting up a Luntclipse connectionTo install the plug-in, first download it from the LuntBuild web site (), unzip it into your Eclipse plug-ins directory, and then restart Eclipse. Alternatively, you can use the remote update site at .Once installed, Luntclipse provides a new view in which you can monitor your LuntBuild server. You can open this view by selecting “Window > Show View > Other... > Luntclipse > LuntBuild.”The first thing you need to do is to set up a connection to your LuntBuild server (see ). This is straightforward: you just need to specify the URL of your LuntBuild server, and a user name and password. You also need to provide the frequency at which the LuntBuild view will be updated from the server.Once you have set up your connection, the LuntBuild view provides a convenient dashboard view of your projects and their builds (see ). Projects and builds are listed in a tree view, with the builds of a project being displayed as children of the project.
Figure : The Luntclipse plug-inYou can consult and manage virtually any aspect of your LuntBuild projects via this view. The contextual menu (see ) lets you perform a variety of tasks such as triggering, searching, moving or deleting builds, consulting logfiles, or even creating or modifying projects. The toolbar at the top of the view provides an alternative way of accessing these functionalities. You can also trigger a build by simply clicking on the build entry (see ).The search function lets you view a set of past builds. This is useful if you want to visualise build history for a certain project over a certain period of time. The list of matching builds is displayed in the “Builds” tab (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Reporting on Test Coverage in Luntbuild Using Cobertura
- InhaltsvorschauContributed by: Jettro ConradieLuntbuild comes out-of-the-box with Junit integration; if your project build runs any JUnit tests, the build view will provide a convenient link to the corresponding JUnit reports. Test coverage reports () can complement these unit test results nicely. Code coverage reports let you investigate how much of your code is actually being tested by your unit tests, and can be an excellent means of improving the quality of your unit tests. There are several code coverage tools available, both open source and commercial. One of the best open source code coverage tools is Cobertura (see ). In this section, we will discuss how to produce and display Cobertura code coverage reports directly from within your Luntbuild build results, right alongside the standard unit test reports. We will also see how to extend Luntbuild functionalities using JavaBeans and Luntbuild extension points.You can extend Luntbuild functionalities fairly easily using simple JavaBean classes. You encapsulate your new functionalities in a standard JavaBean class. You can access your bean from within Luntbuild using OGNL expressions. However, you need to give this file to Luntbuild in a form that it can cope with. Each Luntbuild extension is presented in the form of a JAR file containing the relevant classes along with a special properties file that tells Luntbuild how to integrate this extension. You bundle your bean, and any other classes it needs, into a JAR file containing the special property file named “luntbuild_extension.properties.” This file must contain two property values:Once you have bundled all this into a JAR file, you have your brand new Luntbuild extension! Note that you can only create one extension per JAR file.
- luntbuild.extension.name
- The name of the extension, which you will use in Luntbuild to invoke the extension class.
- luntbuild.extension.class
- The class that implements the extension.
So creating a Luntbuild extension is quite an easy task. Now let’s have a look at the bean we will be using to integrate Cobertura reports into LuntBuild. This JavaBean will basically tell Luntbuild where it can find the coverage report generated by Cobertura:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Integrating Luntbuild with Maven
- InhaltsvorschauContributed by: Jettro ConradieContinuous Integration servers do a good job at, well, continuously building (and integrating) code. Another useful thing that you may want to do is to automatically generate and publish up-to-date project reports about different aspects of the project, such as javadoc, unit test reports, code coverage reports, and so on. You can use both Ant and Maven, both of which integrate well with Luntbuild (see ) to generate this type of report.You can also use Luntbuild to publish these reports for each build, which allows you to automatically generate and publish updated reports on a regular basis, and also to keep track of the reports generated for previous builds. In this section, we will see how to generate and publish a Maven-generated web site, which contains a wide range of quite useful reports. First, we write a Maven Mojo (plug-in class) that enables us to copy the generated Maven web site to the appropriate folder for Luntbuild to pick it up. Then we look at how to add the new Mojo to Maven. Finally, we configure Luntbuild to use our new Mojo to display the Maven web site on the Luntbuild build results pages.The Maven core is actually very small: Maven uses plug-ins, or extensions, to implement most of the heavy-duty functionality. In Maven parlance, these plug-ins are called Mojos, and take the form of annotated Java classes.Writing your Mojo in Java is a big advantage. This way, you can use the programming language you are used to. You can use the tools you are used to and, of course, do junit testing. Reuse of the Mojo over the different projects that you are into is also easy.It is actually quite easy to build your own. In this section, we will go through the steps required to build a simple Mojo plug-in, which will publish your LuntBuild build results on your Maven web site. This Mojo will simply copy the build results from the LuntBuild web site to a designated directory on your Maven site.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Conclusion
- InhaltsvorschauOverall, Luntbuild is a solid, feature-rich Continuous Integration tool with a clean (albeit slightly clunky) web interface, support for a wide range of SCM tools, and a reasonable range of notification techniques.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 8: Continuous Integration with Hudson
- InhaltsvorschauHudson is a relative newcomer to the Continuous Integration (CI) field. However, despite its relative youth, it is probably worth considering for any new CI projects you might be starting. Hosted by Java.net, Hudson is actively developed and maintained by Kohsuke Kawaguchi, who is working for Sun Microsystems at the time of this writing. This innovative product is widely used within Sun, and is starting to build up a sizeable user base because of its ease of use and slick user interface. It has also recently been adopted by JBoss (see ).In many regards, Hudson has considerably fewer features then the some of the other CI tools such as Continuum (see ) and CruiseControl (see ). It concentrates more on Subversion and CVS-based projects, and provides only a limited number of notification techniques. The product is still somewhat young, and a little immature, but the features it does have are extremely well thought-out, with some being quite innovative. Hudson is also extensible, and a growing collection of plug-ins are available on the Hudson web site.Hudson is very easy to install and set up. It requires Java 5 or later. You can download the latest release from the Hudson web site (). Hudson comes bundled as a web archive (WAR) file that you can run directly using an embedded servlet container (Hudson uses the lightweight Winstone servlet engine). To run Hudson from the command line, just type the following:
$ java -jar hudson.war
This will run Hudson on the default 8080 port. If this doesn’t suit you, you can also specify the port using the --httpPort option:$ java -jar hudson.war --httpPort=8081
Running Hudson as a standalone product is a good option for evaluation purposes, but for production-quality deployment, you will probably want to deploy the Hudson WAR onto a more robust Java application server such as Tomcat. This is particularly true when you are installing Hudson on a build server, and you need Hudson to run as a service. You can deploy the Hudson WAR file as you would any other web application. For example, on a Tomcat server, you would deploy theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - An Introduction to Hudson
- InhaltsvorschauHudson is a relative newcomer to the Continuous Integration (CI) field. However, despite its relative youth, it is probably worth considering for any new CI projects you might be starting. Hosted by Java.net, Hudson is actively developed and maintained by Kohsuke Kawaguchi, who is working for Sun Microsystems at the time of this writing. This innovative product is widely used within Sun, and is starting to build up a sizeable user base because of its ease of use and slick user interface. It has also recently been adopted by JBoss (see ).In many regards, Hudson has considerably fewer features then the some of the other CI tools such as Continuum (see ) and CruiseControl (see ). It concentrates more on Subversion and CVS-based projects, and provides only a limited number of notification techniques. The product is still somewhat young, and a little immature, but the features it does have are extremely well thought-out, with some being quite innovative. Hudson is also extensible, and a growing collection of plug-ins are available on the Hudson web site.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Hudson
- InhaltsvorschauHudson is very easy to install and set up. It requires Java 5 or later. You can download the latest release from the Hudson web site (). Hudson comes bundled as a web archive (WAR) file that you can run directly using an embedded servlet container (Hudson uses the lightweight Winstone servlet engine). To run Hudson from the command line, just type the following:
$ java -jar hudson.war
This will run Hudson on the default 8080 port. If this doesn’t suit you, you can also specify the port using the --httpPort option:$ java -jar hudson.war --httpPort=8081
Running Hudson as a standalone product is a good option for evaluation purposes, but for production-quality deployment, you will probably want to deploy the Hudson WAR onto a more robust Java application server such as Tomcat. This is particularly true when you are installing Hudson on a build server, and you need Hudson to run as a service. You can deploy the Hudson WAR file as you would any other web application. For example, on a Tomcat server, you would deploy thehudson.warfile to the directory. You can then configure Tomcat to run as a service in the usual, OS-specific manner.You can check out your new Hudson installation by connecting (e.g., if you are running it locally). The first time you run Hudson, you should get a screen like the one in .
Figure : The Hudson web interfaceEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing the Hudson Home Directory
- InhaltsvorschauHudson stores its data in a special directory, known as the Hudson home directory. By default, the Hudson home directory is called “.hudson.” placed in the user’s home directory. This is not to be confused with the directory in which you place the downloaded hudson.war file, or the expanded Hudson web application on your local application server. Set the_HOME environment variable before you start up Hudson to change the home directory.Let’s look at a practical example of this setup. Your boss wants you to install a Continuous Integration server for your new project. And he wants it to be done by lunchtime (it is now 11 a.m.). No sweat! Suppose we want to install Hudson onto a Linux box, and run it on a local Tomcat server. Let’s see how you might configure your server.First, a word about user accounts. It is often a good idea to run Hudson (as with other build infrastructure tools) in its own dedicated user account. This makes it easier to keep track of resource use on the server, and can be very handy when you have several Java tools running on the same server. For example, in the following listing, we connect to our build server as a special user called “cis” (for “Continuous Integration Server”). First we connect to the server using this user’s account (I cheat a little by su-ing from root, but you get the idea):
# su - cis $ pwd /home/cis $ ls .hudson/ config.xml hudson.triggers.SCMTrigger.xml fingerprints jobs hudson.scm.CVSSCM.xml plugins hudson.tasks.Ant.xml secret.key hudson.tasks.Mailer.xml users hudson.tasks.Maven.xml workspace-cleanup.log hudson.tasks.Shell.xml
Here, we are in this user’s home directory (/home/cis), using the default Hudson home directory of.hudson(/home/cis/.hudson). All of Hudson’s application data and server configuration details are stored in the.hudsondirectory.The Hudson web application is deployed onto a local instance of Tomcat, installed into a directory calledEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing Upgrades
- InhaltsvorschauThe Hudson project team is highly reactive, and new releases come out every few weeks, or even every few days! To upgrade Hudson to a new version, simply download and deploy the latest version to your web application server. As long as you don’t delete your Hudson home directory, your application data won’t be affected.Before you upgrade the web application, you should stop any builds that are currently in progress. You can do this by selecting “Prepare for Shutdown” in the “Manage Hudson” screen.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Configuring Hudson
- InhaltsvorschauIn Hudson, you manage both system-wide parameters and individual projects directly via the Web interface. The “Manage Hudson” screen is the first port of call for all Hudson administrative tasks and serve configuration. From here, you can manage plug-ins, view the system logfile, and display system properties (see ).The most important Hudson parameters are in the “System Configuration” screen (see ). This screen lets you configure the main server-wide parameters such as available Java Development Kits (JDKs) and build tools, security configuration and quiet period.
Figure : The Hudson administration screen
Figure : The Hudson administration screenLet’s look at some of these options. One nice feature in Hudson is that you can configure it to run builds in parallel. The “# of executors” field indicates the maximum number of parallel builds that Hudson will allow. Subsequent builds are queued. This can speed up the build process, to a point. However, builds can be CPU-intensive operations, so too many simultaneous builds can bring your build server to a grinding halt. The precise sweet point will depend on your particular environment, so start off the the default value (up to two builds in parallel) and experiment.As with most other Continuous Integration servers, Hudson lets you define a “quiet period.” This indicates the number of seconds that Hudson will wait after spotting a change in the source code repository, before starting a new build. This is mainly useful with CVS, which doesn’t support atomic commits. This way, Hudson doesn’t rush off straight away to do a new build, but waits until it is sure that there are no more changes to be committed.One of the areas in which Hudson is not particularly strong is in matters of security. By default, Hudson does not impose any security constraints on users. Any user can modify configuration details without needing to be authenticated. This is convenient for small teams, but may not be appropriate for larger organizations. You can however enable security by ticking the “Enable Security” checkbox. In this case, unauthenticated users can only view build results; They cannot launch builds or modify configuration parameters. Only authenticated users with the “admin” role can configure the server or schedule builds. We look at how to configure security in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Adding a New Build Job
- InhaltsvorschauIn Hudson, you organize your Continuous Build environment by defining “build Jobs.” A build job is simply a particular way of building your project. However, to make things simpler, the Hudson user interface also refers to “projects.” In fact, for Hudson, the terms “project” and “job” are synonymous.A software development project typically requires several build jobs, which are used in different circumstances. For example, you may have one build job to compile and run your unit tests, another to run the longer-running integration tests, and a third to perform more heavyweight analysis tasks such as static code analysis and test coverage.To add a new build job, click on the “New Job” link. You can choose from several different types of projects. The most interesting are:You can also create a new project based on an existing one by copying it (quite handy at times), or create a few other types of projects.
- A freestyle project, in which you can customize the whole build process by indicating exactly what you want the build to do. You need to configure pretty much everything. As you might imagine this is the most flexible way to set up a project, and the only way to set up an Ant project.
- A Maven 2 project, which uses the Maven POM file to glean as much information as possible about things such as version control systems and so forth.
This is illustrated in .Let’s go through the process of creating a build project.
Figure : Creating a new project in HudsonThe first project we will build is a freestyle one. In this case, we will set up an Ant-based project stored in a Subversion repository. To get started, we click on “New Job” and choose “Build a freestyle software project” (see ). Here you give the project an appropriate name and click on OK. This will bring you to the screen shown in .The first thing that you need to complete is the project name and description. You can also specify if you want Hudson to delete your old builds after a certain delay, or only keep a certain number of builds. This is useful to avoid cluttering your build environment too much. Come on, how long do youEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Organizing Your Jobs
- InhaltsvorschauIf your Hudson server is hosting several development projects, or even if your project has several modules, you can rapidly end up with an impressive number of projects appearing on the Hudson dashboard. Although Hudson does not have any formal notion of project groups, you can define views to make your dashboard a little clearer. These views appear as tabs on the main dashboard, as shown in .To create a new view, click on the “+” symbol to the right of the tabs on the Hudson home page. You will be able to give the view a meaningful name, and select the build projects that you want to include in this view. Don’t worry if you forget some projects—you can always add more, or remove unwanted projects from the view, via the “Edit View” screen.
Figure : Hudson viewsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Monitoring Your Builds
- InhaltsvorschauOnce you have set up and organized your build projects, you can sit back and relax. The dashboard on the Hudson home page gives you an overview of the state of your projects, using some helpful graphical queues (see ).The colored balls to the left give you an idea of the current state of your build. This is fairly intuitive: blue is good, red is bad, and yellow (not shown in the screen shot) is so-so. More precisely, blue means that your last build was sucessful, and red means that it failed in some spectacular manner. Yellow usually means that there were test failures.
Figure : The dashboard on the Hudson home page.The cute weather report icons aren’t just for decoration—they give you a general picture of the overall health of your project, using data both from the current and from previous builds. This icon is also pretty intuitive—sunny weather is good, cloudy weather means that there are a few issues, and stormy weather means that your project health really needs some attention.The project health indicator takes into account test results, but also code quality metrics such as test coverage, Checkstyle violations, and even the number of TODO tags in the code. The metrics used are primarily up to you, and many of them are provided by plug-ins (see ). If you pass the mouse over the weather icon, you can visualize a more detailed explanation.If you want to know more about the build history of a particular project, just click on the corresponding job name on the dashboard. This will display a summary of the build history for this project, including the latest build artifacts, Javadoc documentation, and test results (see ). You can learn a lot about a build and its history here.The Changes link gives you a rundown of the messages logged in the version control system for each build, which can give you an idea of what changes lead to a particular build.The Test Result Trend graph indicates the number of unit tests run per build. Normally, this should increase over time at a steady rate. If it remains flat too long, you should review the testing habits of your team!Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Viewing and Promoting a Particular Build
- InhaltsvorschauOrganizing releases is a crucial part of any development project. There are whole books on this subject, so we won’t try to cover this in any detail here. Basically, you need to be able to identify and distribute versions of your software in a predictable, controlled way. You need to know what changes went into a given version, and why. Ideally, you should be able to deploy the same artifact to different platforms (test, user acceptance, production, and so on) without modification. This helps to ensure that the product you are delivering is the same one that has passed all your unit, integration, (please look out for missing serial commas) and functional tests. You also need to be able to know what changes were made to the source code, and what issues were addressed.Although Hudson does not claim to be a SCM tool, it does provide some nice features to help keep track of builds and releases. If you click on any build number, Hudson will display a detailed description of that build (see ). From here, you can download the artifact produced by this build. You can also consult the changes made for this build, including log messages and modified files.It is a good habit to tag significant builds in your version control system. However, if you are using CVS, this can be a long and CPU-intensive task, which can take literally hours on a large project. Hudson provides an interface to make this simpler. You can view tags that have already been placed on a build, and add new ones (see ). Using this approach, you have a good idea of what version you are tagging (which is not always so easy from the command line), and the grunt-work is done by Hudson—you just have to provide the tag.
Figure : Displaying the details of a particular build
Figure : Tagging a buildEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing Users
- InhaltsvorschauOne nice thing about Hudson is that it simplifies some of the boring administrative tasks you normally have to do when you maintain a CI server. For example, you don’t need to explicitly identify team members for Hudson—Hudson will figure them out by seeing who’s committing code to the source code repository. You can view these users in the “People” screen (see ).This screen lets you do more than just see who is participating in the development effort, however. If you click on a user, Hudson will bring up a details screen where you can configure the user’s full name (for more readable screens and reports) and email address. This way, Hudson can directly notify individuals who break the build.
Figure : Managing users in HudsonHudson also lets you view the history of each developer’s builds: when and how often they have committed changes, any broken builds, and even details of the build outputs ().
Figure : Hudson makes it easy to view each user’s build historyEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Authentication and Security
- InhaltsvorschauSecurity is not one of Hudson’s strong points. By default, any user who can access the Hudson web site can modify any project configuration, and even the server configuration itself. This is obviously geared toward small, agile, responsible teams rather than large organizations. However, if this is not appropriate, you can enable security by ticking the “Enable Security” checkbox in the Manage Hudson screen.The underlying security mechanism will vary depending on the server where Hudson is running. If you are running Hudson on Tomcat, for example, you can use any of the Tomcat security realms, such as the Tomcat
JNDIRealm, to connect to an LDAP server.User-rights management in Hudson is not very sophisticated. There is no notion of project-specific roles or rights. When secure access is enabled, a user with the “admin” role can schedule builds for any project, and configure the global server configuration. All other users can only view build results.If you really need to isolate projects, one (slightly clumsy) approach is to run a separate Hudson instance for each project or group of projects.As an example, here is part of a simple tomcat-users.xml file. Only the “hudson-admin” user can modify the system configuration or schedule builds:<verbatim> <?xml version='1.0' encoding='utf-8'?> <tomcat-users> ... <role rolename="admin"/> ... <user username="hudson-admin" password="secret" roles="admin"/> </tomcat-users> </verbatim>
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Viewing Changes
- InhaltsvorschauHudson does a good job of keeping track of the changes. When you view the details of a particular build, you can see details of the log messages associated with this build (see ). You can also click on the “detail” link, which will display a list of all the files modified in this build.Hudson also integrates with issue tracking management systems such as Trac and JIRA. In , for example, the server has been configured with the Trac plug-in, which allows it to recognize Trac wiki syntax and add a hyperlink to the issue number referenced in the log message. In addition, the “Trac” link will open up the Trac changeset view corresponding to this build. This sort of integration is very useful for keeping tabs on exactly what has been changed in a particular build.
Figure : Viewing changes in HudsonEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Hudson Plug-Ins
- InhaltsvorschauHudson is particularly extensible, and integrates well with other products. It supports a flexible plug-in architecture, and a rapidly growing number of plug-ins are available on the Hudson web site to add extra functionality and integration with third-party tools. One of the most immediately useful extensions is to integrate Hudson with an issue tracking system such as Trac and JIRA. The Trac plug-in, for example, recognizes Trac wiki links in your log messages, creating links to the corresponding pages in Trac.Other plug-ins provide more sophisticated reporting features. The “violations” plug-in, for example, reports on Checkstyle, PMD, CPD, and FindBugs violations, and the Cobertura plug-in integrates Cobertura coverage reports into Hudson.Plug-ins are easy to install—you simply download the plug-in file from the Hudson web site, and install it onto your Hudson server in the “Manage plug-ins” screen. Alternatively, you can place the plug-in in the $HUDSON_HOME/plug-ins directory on your server. Restart the server and the plug-in will be active.These plug-ins integrate seamlessly into the Hudson interface. They also provide high-quality reporting capabilities similar to those provided by the Maven site generation plugin. These reporting features are an excellent way to provide high-level visibility on your project. At the Maven site generation, these reports can be configured to work with both Ant and Maven projects (although there is still generally more configuration involved for an Ant project than for a Maven one). As such, they are an excellent way to provide consistent reporting on many different projects within an organization.We will look at some of the things you can do with Hudson plug-ins in the following sections.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Keeping Track of Test Results
- InhaltsvorschauIt makes sense to keep track of your unit test results, both in terms of how many tests are being executed, and of the test success rate. This is a useful way of keeping tabs on the number of tests in the project (it should increase over time), and on the number of test failures (which should remain fairly low on the build server).Hudson comes with a useful built-in feature that allows you to record and publish test results in a graphical form. First of all, your project needs to generate JUnit XML test reports. If you are using Ant, you need to configure an XML formatter in your JUnit task (see ). In Maven, XML test reports are generated .Next, you need to tell Hudson where it can find these reports. You do this in the project configuration screen, in the “Post-build Actions” section (see ). Here, you specify a regular expression, relative to your work directory, that will find the XML report files. In a normal Maven project, this will be
target/surefire-reports/*.xml. For an Ant project, it will depend on how you have configured your Ant project.Once you configure this, Hudson will automatically generate and keep track of JUnit test result statistics. The overall test result trends are displayed on the project home screen in Hudson (see , earlier in this chapter). You can also click on the graph to display test results by package or by class (see ).
Figure : Keeping track of JUnit test results
Figure : Hudson displays interactive test result reportsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Keeping Track of Code Metrics
- InhaltsvorschauHudson also provides several useful plug-ins that allow you to keep track of code quality metrics. The most useful of these is probably the Violations plug-in. This plug-in lets you track and display statistics from a number of code analysis tools, including Checkstyle (), PMD and CPD (), and FindBugs ().To activate the Violations plug-in, you need to tick the “Report Violations” checkbox in the “Post-build Actions” section of your project configuration screen (see ).Then you need to tell Hudson where it can find the relevent report XML data. All of these tools can be configured to generate XML reports, and Hudson expects you to provide an Ant-style path pointing to these files.You can also provide two threshold values for each tool, which are used to control the weather report icons visible on the Hudson dashboard. The first, indicated by the sun icon, is the maximum number of violations allowed before the sunny weather report will be replaced by a cloudy one. The second, indicated by the storm icon, specifies the minimum number of violations that will cause the stormy weather report to appear. For example, in , if there are 10 or fewer Checksyle violations, the Checkstyle weather report icon will be sunny. If there are more than 10 but less than 200, the weather will be cloudy. If there are 200 or more, a stormy icon will be displayed.
Figure : Configuring the Hudson Violations plug-inThese weather icons aren’t just used for the violation reports, they are also taken into account in the general weather report on the Hudson dashboard. This lets you integrate code quality metrics into the overall build result status—with too many code quality violations, the dashboard will display a bad-weather icon in the same way as too many successive build failures would do.Once configured, Hudson will display a graphical view of the number of each violation type over time (see ). You can click on the graph to drill down to a more detailed view of the violations, with a report for each type of issue. From here, you can see the current number of violations that each tool has raised, a graph showing the trend over time, and the list of offending files. If you click on one of the files, you can then drill down to a detailed view of the issues raised for a particular file.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Reporting on Code Coverage
- InhaltsvorschauCode coverage (see ) is a useful metric that tells you how much of your application code is being executed during your unit tests. This, in turn, gives you some idea of how well your application is being tested.Hudson provides good reporting capabilities in this area. Plug-ins exist for several code coverage tools, including Clover, a popular commercial code coverage tool, Emma (see ) and Cobertura (both in ). In this section, we will look at how to use the Cobertura plug-in.The Hudson Cobertura plug-in provides excellent reporting on test coverage, showing coverage statistics from the latest build as well as trends in test coverage throughout the life of the project. Like the other metrics tools, this plug-in relies on data generated during the build process, so you need to set up Cobertura in your project before you can go any further. In Ant, you need to generate XML reports explicitly (see ). If you are using Cobertura in a Maven project, the XML reports will be generated automatically (see ).Once your project is correctly configured, you can tell Hudson where to find the Cobertura report data. You configure the Cobertura plug-in in the “Post-build Actions” section of your project configuratiom screen (see ). The most important field is the “Cobertura xml report pattern,” where you need to provide an Ant-style file path that Hudson can use to find the Cobertura XML report. For Maven, this will usually be
**/target/site/cobertura/coverage.xml.For Ant, it will depend on how you have configured the Cobertura task in your build file.The other configuration parameters let you define various coverage metrics that you want to record. You can keep track of many different test coverage metrics, such as package, class, method, line and conditional (or branch) test coverage. This data will be recorded and displayed in the code coverage reports, allowing you to see how you are doing in your latest build, and also letting you observe trends in test coverage.
Figure : Configuring the Hudson Coverage ReportEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 9: Setting Up an Instant Messaging Platform with Openfire
- InhaltsvorschauToday, communication by chat and instant messaging is almost ubiquitous. There is little need to introduce these technologies. However, they are often seen as purely recreational software tools, and their potential as a team communication tool is often ignored. Indeed, chat and instant messaging can be a useful complement to face-to-face meetings, telephones, and emails, without eliminating the need for any of these other communication techniques.This is especially true when team members are scattered across different countries or continents because telephone communications are expensive, and IP telephone is not always feasible because of variable network quality.One solution is to use one of the countless public messaging servers. There are many available, and they work well. However, this approach may be frowned upon by system administrators and/or management who do not wish to see potentially sensitive information circulating outside of the company.The other solution is to install a private messaging server within your organization.This is where Openfire can help. Openfire (formerly known as Wildfire) is a powerful open source Java chat and instant messaging server based on the XMPP (Jabber) protocol. It is simple to install and configure, administration is easy via a slick web console, and it offers an extremely rich range of features.Installing Openfire is easy. Just download the installation package from the Openfire web site and decompress it in an appropriate place. Here we install it in /usr/local on a Linux server:
# tar -xzvf Openfire_3_0_0.tar.gz # mv Openfire /usr/local
If you prefer, it also comes bundled with a JRE in the form of a Windows installer or a Linux RPM.Openfire comes with its own embedded HSQLDB database, which you can use to get up and running quickly. Openfire also lets you use an external database, which can potentially provide better performance. Openfire should work with any JDBC-enabled database, and comes bundled with drivers, scripts, and instructions for some of the more common databases such as MySQL, Oracle, PostgreSQL, and Microsoft SQLServer.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Instant Messaging in a Development Project
- InhaltsvorschauToday, communication by chat and instant messaging is almost ubiquitous. There is little need to introduce these technologies. However, they are often seen as purely recreational software tools, and their potential as a team communication tool is often ignored. Indeed, chat and instant messaging can be a useful complement to face-to-face meetings, telephones, and emails, without eliminating the need for any of these other communication techniques.This is especially true when team members are scattered across different countries or continents because telephone communications are expensive, and IP telephone is not always feasible because of variable network quality.One solution is to use one of the countless public messaging servers. There are many available, and they work well. However, this approach may be frowned upon by system administrators and/or management who do not wish to see potentially sensitive information circulating outside of the company.The other solution is to install a private messaging server within your organization.This is where Openfire can help. Openfire (formerly known as Wildfire) is a powerful open source Java chat and instant messaging server based on the XMPP (Jabber) protocol. It is simple to install and configure, administration is easy via a slick web console, and it offers an extremely rich range of features.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Openfire
- InhaltsvorschauInstalling Openfire is easy. Just download the installation package from the Openfire web site and decompress it in an appropriate place. Here we install it in /usr/local on a Linux server:
# tar -xzvf Openfire_3_0_0.tar.gz # mv Openfire /usr/local
If you prefer, it also comes bundled with a JRE in the form of a Windows installer or a Linux RPM.Openfire comes with its own embedded HSQLDB database, which you can use to get up and running quickly. Openfire also lets you use an external database, which can potentially provide better performance. Openfire should work with any JDBC-enabled database, and comes bundled with drivers, scripts, and instructions for some of the more common databases such as MySQL, Oracle, PostgreSQL, and Microsoft SQLServer.To start up Openfire, just use the Openfire script in the bin directory, as follows:bin/Openfire start
To shutdown the Openfire server, use the same script with the stop option instead:bin/Openfire stop
Finally, to finish your installation, open the administration web site by connecting to http://127.0.0.1:9090/. The administrator account is by default “admin,” with a password of “admin.” When you connect for the first time, you step through a series of screens in which you configure server settings such as language (there are nine to choose from), database configuration, and administrator email and password. When you’ve finished, you can connect to the Openfire Administration Console (see ).
Figure : The Openfire Administration ConsoleEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Users and Accounts on Openfire
- InhaltsvorschauAll your users will need user accounts on the Openfire server. Openfire comes with a fairly intuitive screen where you can create and manage user accounts and user groups (see ).You can also configure Openfire to connect to an LDAP directory (how to do this is well-documented in the Openfire documentation), an external database (see ), or even a POP3 mail server (see ).Your users will, of course, need Jabber-compatible IM/Chat client software installed on their machines. There are hundreds to choose from. Jives Software, the editor behind Openfire, produces a Java-based open source IM client called Spark. Gaim and Kopete are other well-known clients.
Figure : The Openfire User Management screenEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Authenticating Users in an External Database
- InhaltsvorschauYou may need to authenticate against users defined in an external database. Openfire provides a set of classes to authenticate users against a database via a JDBC connection. As with most external authentication mechanisms, this is designed to provide read-only access—users and groups in the external database cannot be modified from within Openfire.All configuration is done in the Openfire.xml configuration file, which you can find in the conf directory. You need to set up three “providers”—for users, groups, and for authentication, respectively:
<provider> <user> <className>org.jivesoftware.Openfire.user.JDBCUserProvider</className> </user> <group> <className>org.jivesoftware.Openfire.group.JDBCGroupProvider</className> </group> <auth> <className>org.jivesoftware.Openfire.auth.JDBCAuthProvider</className> </auth> </provider>Then, you need to describe the SQL queries Openfire will need to do to retrieve the users and groups, and to authenticate users. The jdbcProvider defines the JDBC connection to be used to access the external database:<jdbcProvider> <driver>com.mysql.jdbc.Driver</driver> <connectionString>jdbc:mysql://localhost/mydatabase?user=scott&password=tiger </connectionString> </jdbcProvider>For this example, we will use an external database with three tables—user_account, group, and group_users:CREATE TABLE user_account ( username VARCHAR(64) NOT NULL, password VARCHAR(32), name VARCHAR(100), email VARCHAR(100), PRIMARY KEY (username), ); CREATE TABLE group ( groupname VARCHAR(50) NOT NULL, description VARCHAR(255), PRIMARY KEY (groupname) ); CREATE TABLE group_users ( groupname VARCHAR(50) NOT NULL, username VARCHAR(100) NOT NULL, administrator CHAR NOT NULL, PRIMARY KEY (groupName, username, administrator) );
The jdbcAuthProvider element defines the SQL SELECT statement used to authenticate a user against the external database. You also need to specify how the password is stored—either as plain text (“plain”), or encoded as an MD5 (“md5”) or SHA-1 (“sha1”) hash code:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Authenticating Users Against a POP3 Server
- InhaltsvorschauIf you have a large number of users, it may not be convenient to manage them all by hand in Openfire. If your user mail accounts are stored on a POP3 mail server, Openfire provides the interesting possibility to authenticate users using this server. You need to specify the POP3AuthProvider and POP3UserProvider providers in the Openfire.xml configuration file. You also need to provide some details about the POP3 server:
- <host>
- The name or IP address of your POP3 mail server
- <port>
- The port of the POP3 mail server (110 by default, or 995 for SSL connections)
- <domain>
- The mail domain
- <authRequiresDomain>
- True if your POP3 server requires a full email address when authenticating, or just a username
- <ssl>
- Should an SSL connection be used? (defaults to “false”)
Here is a full example, providing POP3 authentication against a local mail server using an SSL connection:<provider> <auth> <className>org.jivesoftware.Openfire.auth.POP3AuthProvider</className> </auth> <user> <className>org.jivesoftware.Openfire.user.POP3UserProvider</className> </user> </provider> <pop3> <host>pop.mycompany.com</host> <domain>mycompany.com</domain> <authRequiresDomain>true</authRequiresDomain> <ssl>true</ssl> </pop3>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Virtual Team Meetings with the Group Chat
- InhaltsvorschauGroup chat meetings can be particularly useful for dislocated development teams. Although they do not allow for the same types of reactions as a conference call, they require fewer resources and less planning (you don’t need to reserve a meeting room equipped with conference call equipment, for example), and they can leave a convenient written trace of what was discussed, which avoids having to write meeting minutes.Openfire provides some rich functionality in the way of group chat meetings. You create a chat room in the “Group Chat” tab (see ). Openfire lets you choose from a wide range of options. You can set up a moderated room, allow the occupants to invite other users or modify some of the chat room properties, and log the room’s conversations. This last option is probably the most useful in the context of team meetings.Users connect to a chat room on the Openfire server in the same way that they would for a public chat, which largely depends on the IM client they are using. In you can see a typical group chat session, viewed from the Gaim IM client.
Figure : Creating a chat room in Openfire
Figure : Participating in a chat roomEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Extended Functionality with Openfire Plug-Ins
- InhaltsvorschauOpenfire is highly extensible, and there is a growing number of external plug-ins available, which provides extra functionality. The Broadcast plug-in, for example, allows messages to be send to all users. The User Import Export plug-in lets you import and export users in an XML format. And the User Service plug-in allows the user database to be administrated from other applications via HTTP queries.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Using Openfire with Continuum
- InhaltsvorschauInstant messaging technology, in general, and Jabber-based IM, in particular, can be put to imaginative uses within a Java development project. In this section, we will discuss how to integrate Openfire with a continuous integration server. You can use this approach to send IM build failure notifications to developers, for example. Compared to email or RSS notifications, instant messages can provide a faster and more dynamic way of informing developers of build failures, and contribute to shortening the development lifecycle.First of all, you need to set up a dedicated user for your continuous integration server—this is the user who will send the messages, and shouldn’t be one of the other user accounts. Here we will work with Continuum, so we’ll call this user “continuum.” Just create this user as you would any other user from the Openfire administration console (see ).Next, each developer should add this user to his or her list of Contacts. You will also have to open an IM client using the “continuum” account to approve the requests from users to add the “continuum” user to their contact lists.Once this is done, you are ready to configure your Continuum project. If you are using a Maven 2 project, you can configure your Jabber notifiers either from the Continuum web site, or directly within the pom.xml file. Configuring notifications in the pom.xml file is more central, though some users may prefer the web console where all the possible fields are visible. A Jabber notification in a Maven 2 pom.xml file looks like this:
<ciManagement> <system>continuum</system> <notifiers> <notifier> <type>jabber</type> <configuration> <address>mike@mycompany.com</address> <from-address>continuum</from-address> <from-password>continuum</from-password> <host>localhost</host> <port>5222</port> <sslConnection>false</sslConnection> <isGroup>false</isGroup> </configuration> <sendOnError>true</sendOnError> <sendOnFailure>true</sendOnFailure> <sendOnSuccess>true</sendOnSuccess> <sendOnWarning>true</sendOnWarning> </notifier> </notifiers> </ciManagement>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Openfire with CruiseControl
- InhaltsvorschauCruiseControl (discussed in ) is a highly configurable Continuous Integration tool, and provides out-of-the-box support for Jabber IM notifications via the jabber element, which you add as a child of the publishers element in your CruiseControl configuration file (see ). A typical Jabber notification looks like this:
<publishers> <jabber host="localhost" username="cruisecontrol" password="secret" recipient="john@localhost.localdomain" buildresultsurl="http://buildserver:8080/myproject/buildresults" /> </publishers>Here, we use another dedicated Jabber message account, called “cruisecontrol,” just to avoid mixing up the messages. Note that, as in Continuum, notification must be done on a user-by-user basis.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Openfire with Luntbuild
- InhaltsvorschauLuntbuild (discussed in ) has well-thought-out support for Jabber, as it does for most of the other IM protocols. Indeed, of the three tools studied here, it arguably provides the most convenient support for IM messaging notification. Information is well centralized with no unnecessary duplication of data, and everything can be set up conveniently from the web console.First, you set up the Jabber server configuration details in the Properties tab. Luntbuild stores this information, including the server address and the user name and password, centrally. This means that you cannot use two different Jabber servers simultaneously, although this is probably not a common occurrence.Next, you need to assign Jabber accounts to your users. You do this in the Users page, in the “Jabber account” field. Once you define Jabber accounts for your users, Luntbuild will use them for all Jabber notifications, whatever the project.Finally, you need to set up Jabber notification for the appropriate projects. Just go to the Project page, and select the Jabber notification method. While you’re at it, don’t forget to indicate which users should be notified.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Sending Jabber Messages from a Java Application Using the Smack API
- InhaltsvorschauThe underlying technology behind Openfire is the Jabber/XMMP protocol. Jabber is an XML-based open standard for IM and presence services, allowing people or software to exchange messages over the Internet in real time. It is a free and open alternative to proprietary IM protocols such as AIM, MSN, and Yahoo!. There are hundreds of (mostly free) Jabber clients available, as well as many servers such as , , ejabberd, and other open source and proprietary solutions.Jabber uses a very simple XML protocol, supporting several different types of messages—the email-like “normal” messages, chat and groupchat messages used for instant messaging, and headline messages used for ticker-tape-style information such as stock quotes or news headlines. There is also a message type dedicated to error messages. The XML structure used to transmit these messages is clear and concise. Although the general form of the messages is common to all messages, each type of message is slightly different.Despite the name, nomal messages are not what one normally imagines an IM message to be. A normal message is designed to transmit a message to a user who is not necessarily connected, and who is not expected to respond in real time. Many IM clients actually display normal messages in exactly the same way as IM (chat) messages. A typical normal message takes the following form:
<message from="john@myserver.com" to="mike@myserver.com" type="normal" id="message123"> <subject>Greetings</subject> <body>Hi there!</body> </message>The form of this message is typical of all of the message types. The message is represented (appropriately enough) by the <message> element, which takes attributes such as from, to, and id, and provides routing information. The type attribute determines what sort of message is being transmitted. The actual text of the message is provided in the <body> element, which is present in all messages. Other elements, such as <subject> and <thread>, may also be included, depending on the type of message.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Detecting Presence Using the Smack API
- InhaltsvorschauThe Smack API provides several other functionalities, which can be useful if you need to read and process IM messages from your Java code.In the Smack world, a roster is the list of the other IM users you know and with whom you communicate. In IM client software, people in this list are often referred to as contacts, friends, or buddies. You can use this list to identify the users who are currently connected. To do this, you use the getRoster() method, as shown here:
Iterator iter = connection.getRoster().getEntries(); while (iter.hasNext()) { RosterEntry entry = (RosterEntry) iter.next(); System.out.println(entry.getName() + " (" + entry.getUser() + ")"); }This might produce something like the following:mike (mike@myserver.com) (available) john (john@myserver.com) (available) chris (chris@myserver.com)
You can then use this list to send messages to all connected users, or to all connected users in a given domain, for example.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Receiving Messages Using the Smack API
- InhaltsvorschauThe Smack API also provides a framework for receiving and analyzing Jabber messages. You can process incoming messages either synchronously by actively polling a queue of incoming messages, or asynchronously using a listener pattern, depending on your application’s needs. In both cases, filter classes let you limit processing to the precise subset of messages in which you are interested.The org.jivesoftware.smack.PacketListener class lets you set up listeners for incoming messages in order to process them asynchronously. You can use the org.jivesoftware.smack.filter.PacketFilter interface and its implementation classes to build message filters. The Smack API provides a rich set of classes that let you filter on message sender, type, thread, and so on. You can also build up quite complex filter conditions by combining the basic filters using the AndFilter, OrFilter, and NotFilter classes.The actual listening is done with the PacketListener interface. This interface just has one method worthy of interest: the processPacket() method, which is called whenever a message corresponding to the given filter is received. You put it all together by adding the listener instance to your connection using the method.In the following example, we use the PacketListener interface to listen for Messages coming from the “continuum” user. Presumably, in a real application, we would do something sensible with the messages received; here we simply echo the message body to the standard output:
XMPPConnection connection = getConnection(); connection.login(getUsername(), getPassword()); PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter("continuum@localhost.localdomain")); PacketListener myListener = new PacketListener() { public void processPacket(Packet packet) { if (packet instanceof Message) { Message msg = (Message) packet; // Process message System.out.println("Message received, loud and clear:" + msg.getBody()); } } }; // Register the listener. connection.addPacketListener(myListener, filter);Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 10: Testing Your Code with JUnit
- InhaltsvorschauJUnit was a groundbreaking piece of software in its day, and there are many, many useful JUnit extensions that help with unit testing in specialized areas and that still rely on the JUnit 3.x approach. We will look at a few of them later on in the book. This section is a brief refresher on JUnit 3.8, for future reference and to better understand the changes brought by newer frameworks such as JUnit 4 and TestNG ().In JUnit 3, you write unit tests in special Java classes, called test cases. All JUnit 3 test cases must extend the TestCase class. You write unit tests as methods of these classes, following a special naming convention: test methods must return void, take no parameters, and start with the word “test.” Your test classes usually also follow a particular naming convention, such as ending with the word
Test.Here is a simple Unit 3.8 test class that tests a class that calculates GST (“Goods and Services Tax,” also known as a “Value Added Tax” in some countries). Suppose that the standard GST rate is 12.5 percent. Our unit test class might look like this:public class PriceCalculatorTest extends TestCase { public void testCalculateGST() { calculator = new PriceCalculator(); double amountWithGst = calculator.calculatePriceWithGST(100.00); assertEquals("Standard GST is 12.5%", 112.50, amountWithGst, 0.0); } }The TestCase base class comes with a large number of assert methods:assertEquals(),assertTrue(),assertNotNull(), and many more. These make up the core of your unit tests, since they are what you use to actually perform your tests. You use the assert methods to check your obtained results against the expected results. You can optionally provide a message as the first parameter of your asserts, which can help to make it easier to identify the error when you have a lot of unit tests to run.You can override thesetUp()andtearDown()methods (be careful of the capital letters!) to define initialization and housekeeping code that will be run, respectively, before and after each test. For example, if we have many test cases using the calculator object, we might want to create it only once in theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - JUnit 3.8 and JUnit 4
- InhaltsvorschauJUnit was a groundbreaking piece of software in its day, and there are many, many useful JUnit extensions that help with unit testing in specialized areas and that still rely on the JUnit 3.x approach. We will look at a few of them later on in the book. This section is a brief refresher on JUnit 3.8, for future reference and to better understand the changes brought by newer frameworks such as JUnit 4 and TestNG ().In JUnit 3, you write unit tests in special Java classes, called test cases. All JUnit 3 test cases must extend the TestCase class. You write unit tests as methods of these classes, following a special naming convention: test methods must return void, take no parameters, and start with the word “test.” Your test classes usually also follow a particular naming convention, such as ending with the word
Test.Here is a simple Unit 3.8 test class that tests a class that calculates GST (“Goods and Services Tax,” also known as a “Value Added Tax” in some countries). Suppose that the standard GST rate is 12.5 percent. Our unit test class might look like this:public class PriceCalculatorTest extends TestCase { public void testCalculateGST() { calculator = new PriceCalculator(); double amountWithGst = calculator.calculatePriceWithGST(100.00); assertEquals("Standard GST is 12.5%", 112.50, amountWithGst, 0.0); } }The TestCase base class comes with a large number of assert methods:assertEquals(),assertTrue(),assertNotNull(), and many more. These make up the core of your unit tests, since they are what you use to actually perform your tests. You use the assert methods to check your obtained results against the expected results. You can optionally provide a message as the first parameter of your asserts, which can help to make it easier to identify the error when you have a lot of unit tests to run.You can override thesetUp()andtearDown()methods (be careful of the capital letters!) to define initialization and housekeeping code that will be run, respectively, before and after each test. For example, if we have many test cases using the calculator object, we might want to create it only once in theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Unit Testing with JUnit 4
- InhaltsvorschauWhen it comes to unit testing frameworks, JUnit is the de facto standard. It is widely used and known, and has many useful extensions for more specialized testing. JUnit, originally written by Kent Beck and Erich Gamma, is widely recognized as a monumental piece of software that greatly contributed to the popularity (at least in theory) of decent unit testing practices in Java. However, over the last few years, the basic API has not evolved a great deal, and some other different and innovative testing frameworks such as TestNG (see ) have emerged.JUnit 3 imposes many constraints which are no longer justified in the world of Java 5, annotations and IOC programming. In JUnit 3, test classes need to extend a JUnit base class, and tests need to respect a special naming convention: you cannot use any old Java class as a test class. JUnit 3 test classes are initialised each time a test is executed, which makes it harder to refactorise and optimize test code. There is no support for data-driven testing (running your tests against externally provided data). It also lacks features such as dependencies between tests, and test groups.JUnit 4 is a major rewrite of the JUnit API, which aims at taking advantage of the progress in Java technology over the pass few years. It is simpler, easier, and more flexible to use than its predecessor, with a few new features to boot! It introduces a lot of new features that can make writing your unit tests easier, such as the use of and more flexible test class initialization. In JUnit 4, a test can be any old POJO class, and test methods don’t need to respect any particular naming convention.Let’s see how our tax calculator tests (see ) would look in JUnit 4:
import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class PriceCalculatorTest { @Test public void calculateStandardGST() { PriceCalculator calculator = new PriceCalculator(); double gst = calculator.calculatePriceWithGST(100.00); assertEquals(gst, 112.50 , 0.0); } @Test public void calculateReducedGST() { PriceCalculator calculator = new PriceCalculator(); double gst = calculator.calculatePriceWithReducedGST(100.00); assertEquals(gst, 105.00 , 0.0); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up and Optimizing Your Unit Test Cases
- InhaltsvorschauAs with as any other code, unit tests need to be coded efficiently and refactored where necessary. JUnit 4 provides a couple of annotations that can help you out here. The @Before annotation indicates a method that needs to be called before each test, effectively replacing the setup() method of JUnit 3.x. You can also use the @After annotation to indicate any cleanup methods that need to be run after each test. Here, the initialize() method will be called before, and tidyup() after, each unit test:
import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class PriceCalculatorTest { private PriceCalculator calculator; @Before public void initialize() { calculator = new PriceCalculator(); } @Test public void calculateStandardGST() { PriceCalculator calculator = new PriceCalculator(); double gst = calculator.calculatePriceWithGST(100.00); assertEquals(gst, 112.50 , 0.0); } @Test public void calculateReducedGST() { PriceCalculator calculator = new PriceCalculator(); double gst = calculator.calculatePriceWithReducedGST(100.00); assertEquals(gst, 105 , 0.0); } @After public void tidyup() { calculator.close(); calculator = null; } }However, this may still not be optimal. JUnit also provides a few other annotations which you can use to improve things further. Sometimes, for the sake of efficiency, you would like to be able to be able to set up some resources before you run any of the unit tests in a given class, and then clean up afterward. You can do just that with the @BeforeClass and @AfterClass annotations. Methods annotated with @BeforeClass will be invoked just once, before any of the unit tests are executed. And, as you would expect, methods annotated with @AfterClass are executed only when all of the tests have been completed. In the above example, the calculator object should be created only once, at the start of the unit tests, and closed only after all of the tests have been completed. We might add aEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Simple Performance Testing Using Timeouts
- InhaltsvorschauA very simple type of performance testing involves making sure that a particular test always executes within a certain timeframe. This can also be useful for database queries using O/R mapping tools such as Hibernate. Basic errors in Hibernate mapping files, for example, can result in very poor response times, even for relatively simple queries. Although a normal unit test wouldn’t pick this up, a test with a timeout would.This sort of check also works nicely for detecting infinite loops, although it is of course harder to know which parts of your code are likely to contain infinite loops....This technique is directly integrated into the @Test annotation, which allows you to set an upper limit on the amount of time a test may run without failing. To do this, you specify the timeout parameter of the @Test annotation (in milliseconds), as shown here:
@Test(timeout=100) public void lookupGST() { double gst = calculator.lookupRateForYear(2006); assertEquals(gst, GST_RATE_IN_2006 , 0.0); }Now, if the query takes more than 100 milliseconds, the test will fail:Testsuite: com.wakaleo.jpt.alexandria.services.PriceCalculatorTest Tests run: 3, Failures: 0, Errors: 1, Time elapsed: 0.136 sec Testcase: calculateStandardGST took 0.009 sec Testcase: lookupGST took 0.128 sec Caused an ERROR test timed out after 100 milliseconds java.lang.Exception: test timed out after 100 millisecondsFor some critial, high-performance methods, you might want to check that the classes perform with acceptable throughput. Of course, the lower the timeout value, the higher the chances are that some outside factor might slow down your tests and that your tests might timeout incorrectly. For example, in the following test case, we check that on average thecalculateInterest()method never takes more than a millisecond to run:@Test(timeout=50) public void perfTestCalculateInterest() { InterestCalculator calc = new InterestCalculatorImpl(); for(int i = 0 ; i < 50; i++) { calc.calculateInterest(principal, interestRate, startDate, periodInDays); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking for Exceptions the Easy Way
- InhaltsvorschauSometimes you want to check that an exception is correctly thrown under certain circumstances. In JUnit 3.x, this is a fairly laborious task involving catching the exception and asserting success in this case and failure otherwise. In JUnit 4, you can use the expected parameter in the @Test annotation to the exception that should be thrown if all goes according to plan. In the following (somewhat contrived) example, we require the application to go through an IllegalArgumentException if the year is less than some arbitrary year. In JUnit 4, we can check this fairly easily as shown here:
@Test(expected = IllegalArgumentException.class) public void lookupIllegalGSTYear() { double gst = calculator.lookupRateForYear(1066); }Now, if this method doesn’t throw an IllegalArgumentException, the test will fail:Testsuite: com.wakaleo.jpt.alexandria.services.PriceCalculatorTest Tests run: 3, Failures: 1, Errors: 0, Time elapsed: 0.114 sec Testcase: calculateStandardGST took 0.009 sec Testcase: lookupGST took 0.01 sec Testcase: lookupIllegalGSTYear took 0.003 sec FAILED Expected exception: java.lang.IllegalArgumentException junit.framework.AssertionFailedError: Expected exception: java.lang.IllegalArgumentExceptionEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Parameterized Tests
- InhaltsvorschauUnit tests are dull things to write, and developers are prone to take shortcuts. However, there is no escaping that good unit tests need to test business functions with a wide range of different data, be it boundary cases, data classes, or whatever. The same test may well succeed with one set of data, but fail with another. But if a developer needs to write a separate test case for each value (which testing best practices would suggest), he will typically not test very many values. Wouldn’t it be nice to be able to run the same unit test several times, using different data?In fact, JUnit 4 does provide a neat feature that makes it easy to test using arbitrary sets of data. Basically, you can set up a collection of test data and feed it automatically into your unit test methods. Let's look at an example. Suppose that you need to write a class that calculates income tax for a given income in a certain year. The business class face might look like this:
public interface TaxCalculator { public double calculateIncomeTax(int year, double taxableIncome); }Now working out income tax typically involves some pretty nontrivial calculations. Most countries use a progressive tax system of some sort, in which the tax rate increases with the taxable income, using a system of “tax brackets.” In addition, the exact tax brackets sometimes vary from year to year. For this sort of application, it is important to test values within each tax bracket, as well as various boundary cases. So, we will set up a collection of test data containing the income, the year, and the expected income tax, over a wide range of values. Let’s see how we could do this.JUnit 4 lets us define sets of test data, which we can pass to our unit tests. In this case, we need to test various taxable incomes in the different tax brackets. In this example, we will only test against one year (2006), but, in a real-world application, we may want to test against several years. So, our test data sets will contain three values: taxable income, year, and the amount of payable income tax.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using assertThat and the Hamcrest Library
- InhaltsvorschauJUnit 4.4 introduced a new notation for assert statements designed to make the intentions of the developer clearer and easier to read. This notation, originally developed by Joe Walnes, uses the assertThat method, along with set of matcher statements (or constraints, or predicates, depending on your background), which can quite nicely improve the readability of your tests. For example, the following class tests that, in this situation, the calculated tax is zero:
import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; public class TaxCalculatorTest { @Test public void calculateTax() { TaxCalculator calculator = getTaxCalculator(); double calculatedTax = calculator.calculateIncomeTax(2007, 0); assertThat(calculatedTax, is(0.0)); } }Now assertThat(calculatedTax, is(0.0)) is arguably more readable than assertEquals(calculatedTax, 0.0, 0.0), although this may be a matter of personal preference. For me, it certainly reads more naturally. It’s probably the fraction of a second that your brain takes to convert “assertsEquals” into a sentence structure along the lines of “yeah right, so calculated tax has to equal zero.” With the first form, your brain goes “OK, we’re asserting that calculated tax is zero.” This requires less work.More readable tests also mean more reliable and maintainable tests. If your tests are easy to read, it’s easy to see that they are correct.You can use the equalTo matcher (or is, in a shorthand form) as a more readable version of the assertEquals method:String result = "red"; ... assertThat(result, equalTo("red"));These statements can also be combined for more complex tests. For example, using the anyOf matcher statement, the following test checks that the color variable is red, green, or yellow:assertThat(color, anyOf(is("red"),is("green"),is("yellow")));If necessary, you can add a description of your test to make things even clearer:String color = "noir"; assertThat("black is black", color, is("black"));Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - JUnit 4 Theories
- InhaltsvorschauAnother major new feature introduced in JUnit4.4, albeit with an experimental status, is the notion of theories. A theory expresses a general assertion that holds true across a, possibly infinite, number of data sets. Any constraints to the data sets to which the theory applies are specified as assumptions.The developer first specifies a set of data points for testing the theory. A data point is a (generally constant) piece of test data, identified by the @DataPoint annotation. Alternatively, automated tools may analyze the code and automatically create data sets to reinforce or disprove the theory. For example, here we define the years 2007 and 2008 as valid test data:
@DataPoint public static int YEAR_2007 = 2007; @DataPoint public static int YEAR_2008 = 2008;Another set of data can be used to define test data used for possible revenue test data:@DataPoint public static double INCOME_1 = 0.0; @DataPoint public static double INCOME_2 = 0.01; @DataPoint public static double INCOME_3 = 100.0; @DataPoint public static double INCOME_4 = 13999.99; @DataPoint public static double INCOME_5 = 14000.0;To define a theory-enabled test, you use the @Theory annotation instead of the usual @Test annotation. A theory is an ordinary method that takes a certain number of parameters. JUnit will work out which data points to use for the various parameters in your test methods based on their respective types. Each data point will be passed to each parameter of the same type. This can be a little confusing if there are several parameters of the same type. As we will see, you use assumptions to limit the allowed values for each individual parameter.The next step is to define your assumptions using the @assumeThat annotation. Indeed, by placing assumptions within a theory-enabled test case, you limit the test data that will be used to execute that test case. In the following example, we limit a test case to the year of 2007, for incomes greater than $0 and less than $14,000:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using JUnit 4 with Maven 2
- InhaltsvorschauMaven 2 uses the Surefire plug-in to execute unit tests (see ). The Surefire plug-in handles both JUnit 3 and JUnit 4 unit tests seamlessly: the test classes just need to be in the test directory and Maven will automatically detect and run them. You can even combine JUnit 3 and JUnit 4 tests in the same application. You run your unit tests in exactly the same way as you would any other tests in Maven, that is, by using the mvn test command:
$ mvn test [INFO] Scanning for projects... ... ------------------------------------------------------- T E S T S ------------------------------------------------------- ... Results : Tests run: 68, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4 seconds [INFO] Finished at: Tue Aug 14 22:28:51 GMT+12:00 2007 [INFO] Final Memory: 7M/67M [INFO] ------------------------------------------------------------------------
This will run both your JUnit 3 and JUnit 4 tests, and generate the usual set of Surefire reports with the combined results of all your tests. This is very useful if you wish to use JUnit 4 features for your normal unit tests, but still benefit from the many excellent JUnit-3 based test libraries such as StrutsTestCase (see ), the Spring MVC testing framework, or DBUnit.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using JUnit 4 with Ant
- InhaltsvorschauJUnit 4 is poorly supported in any versions of Ant earlier than 1.7.0. From Ant 1.7.0 onward, however, JUnit 4 tests are fully supported and easy to configure. In this section, we will go through the steps of setting up, compiling, and running your JUnit 4 tests using Ant.For completeness, we will go through the whole build file. Most of it will be straightforward to developers familiar with Ant (see ). In the first part of the build file, we just define project directories and housekeeping tasks:
<project name="JUnit-Tests-Sample" default="runtests" basedir="."> <property name="junit.home" value="/home/john/tools/junit4.1" /> <property name="java.src" value="src/main/java" /> <property name="test.src" value="src/test/java" /> <property name="build.dir" value="target" /> <property name="java.classes" value="${build.dir}/classes" /> <property name="test.classes" value="${build.dir}/test-classes" /> <property name="test.reports" value="${build.dir}/test-reports" /> <target name="init"> <mkdir dir="${java.classes}"/> <mkdir dir="${test.classes}"/> <mkdir dir="${test.reports}"/> </target> <target name="clean"> <delete dir="${build.dir}"/> </target>Then, we define a task to compile our Java code:<target name="compile" depends="init" > <javac srcdir="${java.src}" destdir="${java.classes}" > <include name="**/*.java"/> </javac> </target>Again, there is nothing special here: we are just compiling Java code using the standard Ant <javac> task. The next section, however, starts getting a little more interesting. Here, we set up a classpath that refers to the JUnit 4.1 JAR file and the application’s compiled Java classes, and use it to compile the JUnit 4 unit tests. JUnit 4 is with JUnit 3, so unit tests written using the two APIs can comfortably live together in the same project without picking fights:<path id="test.classpath"> <pathelement location="${junit.home}/junit-4.1.jar" /> <pathelement location="${java.classes}" /> </path> <target name="compiletests" depends="compile"> <javac srcdir="${test.src}" destdir="${test.classes}"> <classpath refid="test.classpath" /> <include name="**/*.java"/> </javac> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Selectively Running JUnit 4 Tests in Ant
- InhaltsvorschauThe Ant JUnit task is a flexible tool, and you can use it to pick and choose which unit tests you want to run at a particular time. This section discusses various techniques you can use to do this.The usual way to run your unit tests is en masse, using the <batchtest> element. However, you can run individual tests, using the <test> element:
<target name="runtest" depends="compiletests"> <junit printsummary="yes" haltonfailure="yes"> ... <test name="com.wakaleo.jpt.alexandria.domain.CatalogTest"/> </junit> </target>Invoking this target runs only the unit tests found in this class:$ ant runtest Buildfile: build.xml init: compile: compiletests: runtest: [junit] Running com.wakaleo.jpt.alexandria.domain.CatalogTest [junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 9.02 sec BUILD SUCCESSFULIn this situation, you may well want to exclude this test from your main unit tests, using an <exclude> element as shown here:<target name="runtests" depends="compiletests"> <junit printsummary="yes" haltonfailure="yes"> ... <batchtest fork="yes" todir="${test.reports}"> <fileset dir="${test.src}"> <include name="**/*Test*.java"/> <exclude name="**/CatalogTest.java"/> </fileset> </batchtest> </junit> </target>In some cases, you may prefer not to run all of your test classes each time you run your normal unit tests. Some types of tests—such as load tests, integration tests and performance tests—can be time-consuming, and need not be run each time you recompile your application. Developer unit tests should be kept short and snappy, and the slower, more processor-intensive tests should only be run when needed. The latter tests are typically run only on request on the developer’s machines but systematically on the integration server.One way to do this is to use the if attribute of the <batchtest> element. This attribute specifies a property that must be set for the unit tests to run; otherwise, they are simply skipped.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Integration Tests
- InhaltsvorschauUnit tests should be short and snappy, and give a quick turnaround time. Unit tests shouldn’t use external resources such as databases or web application frameworks. To do this, you often use interfaces, mock objects, and a variety of other techniques to ensure that you are testing each component in isolation.However, you will need to see how the components fit together at some point. This point is called the “integration tests” phase. Here, you might test your Hibernate-based DAOs against a real database (as opposed to an embedded one), run queries that go all the way from the service layer to the database and back, or simulate a user’s web browser using a tool like Selenium. You might also want to see how the application holds up under load, or with many simultaneous requests. These tests are vitally important, but they would slow things down far too much if they had to be run every time a developer runs through the ordinary unit tests. Slow unit tests discourage developers from testing, so you need a way to distinguish fast unit tests from the slower integration tests.In Maven, you can configure the Surefire plug-in to determine what tests are run during the unit tests phase (when you run mvn test), and which ones are run during the integration tests (when you run mvn integration-test). In the following example, integration tests have names that end in “IntegrationTest.” This is a simple, arbitrary convention: you can, of course, define your own. The following configuration excludes the integration tests from the ordinary unit tests (associated with the “test” phase), and attaches them to the “integration-test” phase instead:
<project> ... <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <executions> <execution> <id>unit-tests</id> <phase>test</phase> <goals> <goal>test</goal> </goals> <configuration> <excludes> <exclude>**/*IntegrationTest.java</exclude> </excludes> </configuration> </execution> <execution> <id>integration-tests</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <includes> <include>**/*IntegrationTest.java</include> </includes> </configuration> </execution> </executions> </plugin> </plugins> </build> ... </project>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using JUnit 4 in Eclipse
- InhaltsvorschauWorking from within your IDE is probably the easiest and most productive way to run your unit tests. JUnit 4 integrates extremely smoothly with Eclipse: you run JUnit 4 tests in exactly the same way as for JUnit 3 tests, by using “Run As...JUnit Test.” If you are using Java 5-style asserts, you will also need to place the -ea (“enable assertions”) option in the Run configuration screen (see ). Otherwise, your asserts will simply be ignored.
Figure : Configuring JUnit 4 test runs to use assert operationsEclipse even handles JUnit 4-specific features such parameterized tests correctly (see ).
Figure : Running JUnit4 tests in EclipseYou can also create new JUnit4 test cases easily in Eclipse by selecting “New...JUnit Unit Test” (see ). This dialog box lets you create either JUnit 3.8 or JUnit 4 test classes (you can uses both types of unit tests simultaneously in the same project).
Figure : Creating a new JUnit4 test in EclipseEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 11: Next-Generation Testing with TestNG
- InhaltsvorschauTestNG is an innovative unit test framework, written by Cédric Beust and Alexandru Popescu, designed to overcome many of the perceived shortcomings of JUnit 3 (see ). Like JUnit 4, TestNG improves on JUnit 3 in many ways, removing syntactical constraints and adding flexible, nonintrusive annotation-based testing. In addition, TestNG also supports many powerful features such as test groups, dependencies, and parallel testing—features that are not yet supported by 4 at the time of this writing. TestNG integrates well with IDEs such as Eclipse and build tools like Ant and Maven. All of these features make it easier to write better-designed, faster and more flexible unit .Before discussing the more advanced features of TestNG, we will take a quick introductory tour. Let’s look at how to get started with TestNG.TestNG classes are ordinary Java classes with ordinary methods: They don’t have to extend a particular class, nor do their methods have to follow any particular naming convention. You simply use the @Test annotation to flag unit test methods, and use Java 5 asserts to test calculated values against expected ones. Throughout this chapter, we will use the example of a class that calculates GST (goods and services tax, also known as a value added tax in some countries), which we introduced in . This class is supposed to calculate the net price of something, taking into account the current GST rate. Suppose that the standard GST rate is 12.5 percent. Our unit test class might look like this:
import org.testng.annotations.Test; public class PriceCalculatorTests { @Test public void calculateGST() { PriceCalculator calculator = new PriceCalculator(); double amountWithGst = calculator.calculateGST(100.00); assert (112.50 == amountWithGst) : "Standard GST should be 12.5%"; } }You may prefer the old JUnit 3 assert methods (assertEquals(), and so on). TestNG via also supports these by using static imports. This approach is also used by JUnit 4 (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introducing TestNG
- InhaltsvorschauTestNG is an innovative unit test framework, written by Cédric Beust and Alexandru Popescu, designed to overcome many of the perceived shortcomings of JUnit 3 (see ). Like JUnit 4, TestNG improves on JUnit 3 in many ways, removing syntactical constraints and adding flexible, nonintrusive annotation-based testing. In addition, TestNG also supports many powerful features such as test groups, dependencies, and parallel testing—features that are not yet supported by 4 at the time of this writing. TestNG integrates well with IDEs such as Eclipse and build tools like Ant and Maven. All of these features make it easier to write better-designed, faster and more flexible unit .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Creating Simple Unit Tests with TestNG
- InhaltsvorschauBefore discussing the more advanced features of TestNG, we will take a quick introductory tour. Let’s look at how to get started with TestNG.TestNG classes are ordinary Java classes with ordinary methods: They don’t have to extend a particular class, nor do their methods have to follow any particular naming convention. You simply use the @Test annotation to flag unit test methods, and use Java 5 asserts to test calculated values against expected ones. Throughout this chapter, we will use the example of a class that calculates GST (goods and services tax, also known as a value added tax in some countries), which we introduced in . This class is supposed to calculate the net price of something, taking into account the current GST rate. Suppose that the standard GST rate is 12.5 percent. Our unit test class might look like this:
import org.testng.annotations.Test; public class PriceCalculatorTests { @Test public void calculateGST() { PriceCalculator calculator = new PriceCalculator(); double amountWithGst = calculator.calculateGST(100.00); assert (112.50 == amountWithGst) : "Standard GST should be 12.5%"; } }You may prefer the old JUnit 3 assert methods (assertEquals(), and so on). TestNG via also supports these by using static imports. This approach is also used by JUnit 4 (see ).import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; public class PriceCalculatorTests { @Test public void calculateGST() { PriceCalculator calculator = new PriceCalculator(); double amountWithGst = calculator.calculateGST(100.00); assertEquals("Standard GST should be 12.5%", amountWithGst, 112.50 , 0.0); } }Test frameworks generally let you define code that needs to be run before every test. In JUnit 3.x, you override the setUp() and tearDown() methods. TestNG comes with a rich set of annotations for different types of fixture code (see ). In the following example, we use the @BeforeMethod annotation to ensure that theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Defining TestNG Test Suites
- InhaltsvorschauTraditionally, TestNG unit tests are generally organized into test suites. In TestNG, a test suite is a logical set of tests that you expect to run together. You define TestNG test suites using a TestNG configuration file, an XML configuration file that tells TestNG how your tests are organized and where it needs to look for them. This is a little more complicated than a JUnit test configuration, where, if you are using Maven (see ) or Ant (see ), you can get away with just writing test classes and placing them in the test source code directory, without having to set up a TestNG configuration file. But it’s sometimes worth the extra effort of using TestNG configuration files for the flexibility and readability it gives you.Here is a simple test suite configuration file. Here, we simply list the test classes we want to test:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="Suite" verbose="2" > <test name="Domain tests" annotations="JDK"> <classes> <class name="com.wakaleo.jpt.hotel.domain.PriceCalculatorTests"/> <class name="com.wakaleo.jpt.hotel.domain.HotelTests"/> <class name="com.wakaleo.jpt.hotel.domain.CityTest"/> <class name="com.wakaleo.jpt.hotel.domain.CountryTest"/> </classes> </test> </suite>TestNG supports both Java 5 annotations and the older Javadoc-style annotations for JDK 1-4. The annotations=“JDK” attribute in the previous listing indicates that we are working with Java 5 annotations, which is what we will use throughout the rest of this chapter.You can also specify a list of packages rather than a list of classes, which will run all the tests contained in the specified package:<suite name="Suite" verbose="2" > <test name="Domain tests" annotations="JDK"> <packages> <package name="com.wakaleo.jpt.hotel.domain" /> </packages> </test> </suite>To make things even simpler, you can use wildcard characters in package and class names. The following test suite includes any classes in any packages directly or indirectly contained in theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The TestNG Eclipse Plug-In
- InhaltsvorschauOne of the most effective ways to run unit tests is from within an IDE such as Eclipse or NetBeans. This approach allows a more seamless development process and a tighter testing cycle. At the time of this writing, TestNG plug-ins exist for Eclipse and IntelliJ. In this section, we will look at how to install and use the Eclipse TestNG plug-in. This excellent piece of software provides invaluable tools for writing, maintaining, and running your TestNG test cases from within Eclipse.You install the TestNG plug-in from the Remote Update site in the usual way:Once you have finished, you may need to restart Eclipse. You can verify the installation by opening the TestNG view (Windows→Show View→Other…), as shown in . This will open the TestNG dashboard at the bottom of the screen.
- Open the “Install/Update” window (Help→Software updates→Find and Install) and select “Search for new features to install.”
- Create a “New Remote Site.”
- Enter http://beust.com/eclipse for the URL and “TestNG Plugin” (or some other appropriate name) for the name.
- Make sure the “TestNG Plugin” checkbox is checked in the site list, and click “Finish.”
- In the next window, check the “TestNG” box in the features to install, and step through the installation process.
Figure : Opening the TestNG view in EclipseRunning a TestNG test class, or an individual TestNG-annotated unit test in Eclipse is intuitive and easy, and very similar to what you would do for a JUnit test case: just select the class or method in the Outline view and select “Run As→TestNG Test” in the contextual menu (see ). This will run the corresponding tests and display the results in the Console view and in the TestNG results view (see ). If you want to step through the code using the IDE debugging perspective, select “Debug As→TestNG Test” instead.
Figure : Running TestNG tests in EclipseRunning multiple tests is a bit more involved. One way is to use a test suite (see ). First of all, you need a TestNG configuration file which defines the tests you want to run. Then, just select the configuration file in the Outline view, and select “Run As→TestNG Suite” in the contextual menu. This will run all of the tests defined in this test suite.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using TestNG in Ant
- InhaltsvorschauIn a professional development environment, it is important to be able to run your tests in a reproducible and predictable manner. The best way to do this is to integrate your tests into your build process, whether it be using Ant, Maven, or some other build tool. TestNG works neatly with both Ant and Maven. Here we will look at how to use TestNG in Ant.TestNG comes bundled with a convenient Ant task that lets you run TestNG tests, groups of tests, or test suites, in a very flexible manner. In the rest of this section, we will go through a practical example of what you can do with this task.You define the TestNG task in a fairly standard manner, using the Ant <taskdef> task and indicating the location of the TestNG JAR file. In this example, I installed TestNG 5.3 in the /usr/local/testng directory, and we are interested in the JDK 1.5 version of TestNG. So we could set up the Ant <taskdef> as follows:
<!-- TestNG resources --> <property name="testng.home" value="/usr/local/testng" /> <property name="jdk15.testng.jar" value="${testng.home}/testng-5.5-jdk15.jar" /> <taskdef resource="testngtasks" classpath="${jdk15.testng.jar}"/>The <taskdef> instruction defines the <testng> task, which you can use to run your unit tests in a number of ways. First, however, you need to compile your classes and test classes in the usual way. In a real-world Ant project, the way you would do this can vary quite a bit, particularly with regards to directory structures. For completeness, an example of these tasks is shown here. The directory structure is represented by Ant properties: java.src is the root directory containing the application source code; test.src is the root directory containing the unit test source code; java.classes and test.classes contain the compiled application classes and unit tests, respectively:<target name="init" description="Create required work directories"> <mkdir dir="${java.classes}"/> <mkdir dir="${test.classes}"/> <mkdir dir="${test.reports}"/> </target> <target name="compile" depends="init" description="Compile application classes"> <javac srcdir="${java.src}" destdir="${java.classes}" includes="**/*.java" /> </target> <path id="tests.classpath"> <pathelement location="${jdk15.testng.jar}" /> <pathelement location="${java.classes}" /> </path> <target name="compiletests" depends="compile" description="Compile test classes"> <javac srcdir="${test.src}" destdir="${test.classes}" classpathref="tests.classpath" includes="**/*.java" /> </target> <path id="runtests.classpath"> <path refid="tests.classpath"/> <pathelement location="${test.classes}" /> </path>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using TestNG with Maven 2
- InhaltsvorschauMaven (or, more precisely, Surefire) recognizes TestNG test cases. In other words, Maven should pick up TestNG tests in exactly the same way as JUnit 3 and JUnit 4 tests.TestNG dependencies in Maven are a little quirky. There are actually two distributions of each version of TestNG: one compiled for JDK 1.5, and one compiled for JDK 1.4. The APIs are quite different: the first uses Java 5 annotations, whereas the second uses the old javadoc-style annotations.In the Maven repository, the TestNG 5.1 jars (the latest at the time of this writing) are called testng-5.1-jdk15.jar and testng-5.1-jdk14.jar, for JDK 1.5 and JDK 1.4, respectively. When you add a TestNG dependency to your Maven POM file, you need to specify which version you need, using the <classifier> element. In the following example, we are using TestNG 5.1, compiled for JDK 1.5:
<dependencies> ... <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>5.1</version> <classifier>jdk15</classifier> <scope>test</scope> </dependency> ... </dependencies>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing the Test Lifecycle
- InhaltsvorschauWhen you write unit tests for real-world applications, you often find yourself writing a lot of supporting code, which is generally designed to prepare a clean, predicable test environment for your tests to run in, and to tidy up afterward. This code is often referred to as “fixture” code. Writing this code well is crucial to successful, efficient testing, and it often represents a sizeable and time-consuming part of your test classes. TestNG provides a powerful set of annotations that let you write flexible fixture code that can be executed at various moments during the test lifecycle.Unit testing frameworks such as TestNG or JUnit are designed to run your tests in an organized, predictable manner. The test lifecycle defines the way and the order in which your test classes are instantiated and your unit tests executed. Understanding the test lifecycle can help you write better, faster, and more maintainable unit tests.TestNG gives you a great deal of control over the test lifecycle. You can define methods which are executed at virtually any point in the unit test lifecycle: before and after the unit tests themselves, but also before and after executing the tests in a particular class or in a test suite. You can also set up methods which must be run before and after tests in a given group (see ) are executed. In this section, we will look at how to use these fixture annotations to improve the quality and speed of your unit tests.
Figure : The TestNG test lifecycle and corresponding annotationsIn TestNG, tests are organized into Test Suites. Each Test Suite is made up of test classes, which contain a number of unit tests, encoded within some test methods. TestNG provides annotations that let you insert fixture code before and after each of these components (see ).Let’s look at a some concrete examples.One of the main uses of fixture code is to prepare a clean test environment for each unit test. In TestNG, methods tagged with the @BeforeTest andEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Test Groups
- InhaltsvorschauOne of the more popular features of TestNG is its support for test groups. Test groups are useful in many situations, and they provide a degree of flexibility that opens the door to a whole new way of thinking about testing strategies. A typical example is when you need to distinguish between fast, lightweight tests (using mock-objects, for example) that are to be run regularly on the developer’s machines, and longer, more complete integration and/or performance tests that need to be run on the server. You may need to distinguish between tests that must be run on a particular platform or environment. You may use groups to identify tests that run against certain parts of the system: database tests, user interface tests, and so on.In TestNG, you can declare unit tests to belong to one or several groups. Then you can run certain groups of tests at different times or in different places. You add a method, or even an entire class, to a group by using the groups parameter in the @Test . The syntax lets you add a class or method to one or several groups. Naming a group in a @Test annotation somewhere is all you need to do to bring it into existence—there is no “One File to Rule Them All”-style configuration file defining your groups:
@Test(groups = "unit-test, integration-test" ) public void testAnotherExchangeRate() { ... } @Test(groups = { "integration-test" }) public void testBatchProcessExchangeRates() { ... }Adding an entire class to a group is a good way of defining a default group for all the unit tests within that class (it saves typing and fits in well with the DRY [“Don’t Repeat Yourself”] principle). We could simplify the previous example by adding the class to the “integration-test” group, as shown here:@Test(groups = { "integration-test" }) // All tests in this class should be considered as integration tests public class CurrencyConverterTest { @Test(groups = "unit-test") // This one is a unit test too public void testAnotherExchangeRate() { ... } @Test public void testBatchProcessExchangeRates() { ... } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing Dependencies
- InhaltsvorschauTest method dependencies are another area in which TestNG excels. Dependencies let you ensure that your test and fixture methods run in a particular order. This is clearly useful when you need to run your fixture methods in a certain order. For example, you may need to create an in-memory test database before filling it with test data. Another use is to create dependencies among tests. Sometimes, for example, if a particular key test in a test class fails, there is little point in running certain other tests. In this section, we look at how you can make the most out of test dependencies in TestNG.One of the most common uses of TestNG dependencies is to coordinate test fixture code. Fixture code (see ) is designed to set up and configure a predictable test environment before running your tests, and to clean up afterward. This sort of code plays an important role in any but the most trivial unit tests, and it is important that it be reliable and easily maintainable. For example, suppose that we need to run a series of tests against an in-memory database, with a predetermined set of test data. These tests are read-only, so we need to setup the database only once, at the start of the test class. One way of doing this would be to use the @BeforeClass annotation with some methods written to perform these two tasks. The code might look something like this:
... @BeforeClass public void createTestDatabase() { ... } @BeforeClass public void prepareTestData() { ... } ...Now, it is fairly obvious that we need to create the database before we insert the test data. At first glance, the above code seems OK in this regard. However, there is actually no guarantee that the createTestDatabase() method will be called before the prepareTestData() method. In TestNG, methods will respect the lifecycle-related ordering discussed in , but, within a given phase (say, the @BeforeClass methods) the order of execution is not determined. And, in any case, it would be nicer to be able to define this sort of dependency .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Parallel Testing
- InhaltsvorschauParallel testing is the ability to run unit tests simultaneously in several different threads. This fairly advanced testing technique can be useful in many situations. In web development, your application will typically be used by many concurrent users, and code may be required to run simultaneously on several threads. One of the best ways to check that your code supports this kind of simultaneous access is to run multithreaded unit tests against it. Multithreaded unit tests are also a good way to do low-level performance testing.TestNG provides excellent built-in support for parallel testing. Multithreaded testing can be set up directly using the @Test annotation, using two main parameters: threadPoolSize and invocationCount. The threadPoolSize parameter lets you run unit tests from a number of threads running in parallel. The invocationCount parameter determines the number of times a test method will be executed in each thread. In the following example, TestNG will set up five threads (the threadPoolSize parameter). Each thread will run (or “invoke”) the testConcurrentHotelSearch() method 10 times (the invocationCount parameter):
@Test(threadPoolSize = 5, invocationCount = 10, timeOut = 1000) public void testConcurrentHotelSearch() { HotelSearchService hotelSearch = (HotelSearchService) beanFactory.getBean("hotelSearch"); List<Hotel> hotels = hotelSearch.findByCity("Paris"); ... }The timeOut parameter, which we see here, is useful for performance testing. It indicates the maximum time (in milliseconds) that a test method should take to run. If it takes any longer than this, the test will fail. You can use it with any test method, not just with multithreaded tests. However, when you are running multithreaded tests, you can use this parameter to guarantee that none of the threads will ever block the test run forever.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Test Parameters and Data-Driven Testing
- InhaltsvorschauGood testing involves more than simply exercising the code. You may also need to test your code against a wide range of input data. To make this easier, TestNG provides easy-to-use support for data-driven testing.For example, suppose we want to test a business method that returns a list of hotels for a given city. We want to test this method using a number of different cities, making sure that the resulting list contains only hotels for that city. Let’s see how we could do this using TestNG.First of all, you need to set up a data provider. A data provider is simply a method which returns your test data, in the form of either a two-dimentional array of Objects (Object[][]) or an Iterator over a list of objects (Iterator<Object[]>). You set up a data provider by using (appropriately enough) the @DataProvider annotation, along with a unique name, as shown here:
@DataProvider(name = "test-cities") public Object[][] fetchCityData() { return new Object[][] { new Object[] { "London" }, new Object[] { "Paris" }, new Object[] { "Madrid" }, new Object[] { "Amsterdam" } }; }Next, you can use this data provider to provide parameters for your test cases. TestNG tests can take parameters, so writing a test case that works with a data provider is easy. You simply create a test method with the correct number of parameters, and specify the data provider using the dataProvider parameter in the @Test annotation:@Test(dataProvider="test-cities") public void findHotelsInCity(String city) { List<Hotel> results = hotelFinder.findInCity(city); // Check that every hotel in this list belongs to the specified city. ... }Now, if we run this, TestNG will invoke this method as many times as necessary for each data entry returned by the data provider:$ ant runtests Buildfile: build.xml ... runtests: ... PASSED: findHotelsInCity("London") PASSED: findHotelsInCity("Paris") PASSED: findHotelsInCity("Madrid") PASSED: findHotelsInCity("Amsterdam") ...Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking for Exceptions
- InhaltsvorschauTesting proper error handling is another aspect of unit testing. Indeed, you sometimes need to check that a particular exception is correctly thrown under certain circumstances (see ). In TestNG, you can do this easily by using the expectedExceptions annotation parameter. You just specify the Exception class which should be thrown by your test in this parameter, and TestNG does the rest. If the exception is thrown, the test passes; otherwise, it fails. In the following example, we test a method that searches for hotels in a given country. The country code comes from a pre-defined list, and if an illegal country code is provided, the method should throw an UnknownCountryCodeException. We could test this error handling process as follows:
@Test(expectedExceptions = UnknownCountryCodeException.class) public void lookupHotelWithUnknownCountryCode() { HotelSearchService hotelSearch = (HotelSearchService) beanFactory.getBean("hotelSearch"); List<Hotel> hotels = hotelSearch.findByCountryCode("XXX"); }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Handling Partial Failures
- InhaltsvorschauOne tricky case to test is when you know that a certain (generally small) percentage of test runs will fail. This often occurs in integration or performance testing. For example, you may need to query a remote web service. The response time will be dependant on many factors: network traffic, the amount of data sent over the network, the execution time of the remote request, and so on. However, according to your performance requirements, you need to be able to perform this operation in less than 50 milliseconds, at least 99 percent of the time.So, how do you test this in TestNG? It’s actually pretty simple. TestNG provides a successPercentage parameter, which you use in conjunction with the invocationCount to verify that at least a given percentage of tests succeed. This is one way that we might test the performance requirements described above:
@Test(invocationCount=1000, successPercentage=99, timeOut=50) public void loadTestWebserviceLookup() { .... }You obviously need the invocationCount parameter to be high enough to give statistically significant results. Ten tests are generally not enough, and even a hundred tests will still present a fair bit of statistical variation. Although it will depend on your , you will usually need several hundred or thousand tests to be able to have any degree of statistical reliability.It is also a good idea to place this sort of test in a special group reserved for long-running performance and load tests. And for more realistic testing, you can also toss in the threadPoolSize parameter (see ) to run your tests in parallel and simulate a multiuser environment.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Rerunning Failed Tests
- InhaltsvorschauLarge real-world applications will often contain hundreds or thousands of test cases. And it can be particularly frustrating when you have to rerun the entire test suite just because two or three tests have failed. Wouldn’t it be nice if you could simply fix the code and only run the tests that failed? TestNG provides a neat little feature to do just this: You have the option of rerunning only the test methods that failed the last time round.Whenever TestNG comes across a test failure, it creates (or appends to) a special test suite configuration file in the output directory called testng-failed.xml. Once all of the tests have been executed, this file will contain the complete list of the test methods which have failed. Then, once you have corrected your code, you just have to run TestNG using this configuration file and TestNG will rerun the failed tests. Needless to say, this can be an invaluable time-saver.To run this from Ant (see ), you could just add a simple target that runs TestNG against the testng-failed.xml configuration file, as shown here:
<target name="failed-tests" depends="compiletests" description="Run TestNG unit tests"> <testng classpathref="runtests.classpath" outputDir="${test.reports}" verbose="2" haltonfailure="true"> <xmlfileset dir="${test.reports}" includes="testng-failed.xml"/> </testng> </target>The practice of rerunning failed tests is not of course designed to replace comprehensive unit and regression tests. It is always possible that a correction somewhere may have broken a test somewhere else, so you should still run the entire test suite at some point to ensure that everything is still working. However, if you need to fix just one or two errors out of hundreds of test cases, this is a big time-saver.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 12: Maximizing Test Coverage with Cobertura
- InhaltsvorschauUnit testing is recognized as a crucial part of modern software development practices. Nevertheless, for a number of reasons discussed at length elsewhere, it is often done insufficiently and poorly. Basically, there are two main things that can make a unit test ineffective: it can execute the code but test the business logic poorly or not at all, or it can neglect to test parts of the code. The first case is fairly hard to detect automatically. In this chapter we will look at the second type of issue, which is the domain of test coverage tools.It is fairly clear that if a part of your code isn’t being executed during the unit tests, then it isn’t being tested. And this is often a Bad Thing. This is where test coverage tools come it. Test coverage tools observe your code during unit tests, recording which lines have been executed (and therefore subject to at least some testing). And although the fact that a line of code is executed during unit tests offers absolutely no guarantee that it executes correctly (it is easy enough to write unit tests that exercise an entire class without testing any business logic at all!), in practice it is always preferable to minimize the amount of code that is not tested at all.Cobertura is a free, open source test coverage tool for Java. Cobertura works by instrumenting the compiled bytecode from your application, inserting code to detect and log which lines have and have not been executed during the unit tests. You run your unit tests normally, and the inserted bytecode logs the execution details. Finally, using these logs, Cobertura generates clear, readable test coverage reports in HTML. These reports provide a high-level overview of test coverage statistics across the entire project, and also let you drill down into individual packages and classes, and inspect which lines of code were and were not executed, allowing developers to correct or complete unit tests accordingly. Cobertura also measures complexity metrics such as McCabe cyclomatic code complexity.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Test Coverage
- InhaltsvorschauUnit testing is recognized as a crucial part of modern software development practices. Nevertheless, for a number of reasons discussed at length elsewhere, it is often done insufficiently and poorly. Basically, there are two main things that can make a unit test ineffective: it can execute the code but test the business logic poorly or not at all, or it can neglect to test parts of the code. The first case is fairly hard to detect automatically. In this chapter we will look at the second type of issue, which is the domain of test coverage tools.It is fairly clear that if a part of your code isn’t being executed during the unit tests, then it isn’t being tested. And this is often a Bad Thing. This is where test coverage tools come it. Test coverage tools observe your code during unit tests, recording which lines have been executed (and therefore subject to at least some testing). And although the fact that a line of code is executed during unit tests offers absolutely no guarantee that it executes correctly (it is easy enough to write unit tests that exercise an entire class without testing any business logic at all!), in practice it is always preferable to minimize the amount of code that is not tested at all.Cobertura is a free, open source test coverage tool for Java. Cobertura works by instrumenting the compiled bytecode from your application, inserting code to detect and log which lines have and have not been executed during the unit tests. You run your unit tests normally, and the inserted bytecode logs the execution details. Finally, using these logs, Cobertura generates clear, readable test coverage reports in HTML. These reports provide a high-level overview of test coverage statistics across the entire project, and also let you drill down into individual packages and classes, and inspect which lines of code were and were not executed, allowing developers to correct or complete unit tests accordingly. Cobertura also measures complexity metrics such as McCabe cyclomatic code complexity.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Running Cobertura from Ant
- InhaltsvorschauCobertura integrates well with Ant: with a little configuration, you can have all the power and flexibility of the tool at your fingertips. Let’s look at how to integrate Cobertura into an Ant project.First of all, you need to install Cobertura. Just download the latest distribution from the Cobertura web site and extract it into an appropriate directory. On my machine, I installed Cobertura into /usr/local/tools/cobertura-1.8, and added a symbolic link called
/usr/local/tools/cobertura.Cobertura comes bundled with an Ant task. You just need to define this task in your build.xml file as follows:<property name="cobertura.dir" value="/usr/local/tools/cobertura" /> <path id="cobertura.classpath"> <fileset dir="${cobertura.dir}"> <include name="cobertura.jar" /> <include name="lib/**/*.jar" /> </fileset> </path> <taskdef classpathref="cobertura.classpath" resource="tasks.properties" />The next step is to instrument your files. You can do this using the cobertura-instrument task:<property name="instrumented.dir" value="${build.dir}/instrumented-classes" /> <target name="instrument" depends="compile"> <mkdir dir="${instrumented.dir}"/> <delete file="${basedir}/cobertura.ser" /> <cobertura-instrument todir="${instrumented.dir}" datafile="${basedir}/cobertura.ser"> <fileset dir="${build.classes.dir}"> <include name="**/*.class" /> <exclude name="**/*Test.class" /> </fileset> </cobertura-instrument> </target>This task is fairly simple. It is good practice to place the instrumented classes into a different directory than the normal compiled classes. In this case, we generate them in the instrumented-classes directory, using the todir option.Cobertura stores metadata about your classes in a special file, called by default cobertura.ser. Here, we use the datafile option to avoid any confusion (we will need to refer to exactly the same metadata file when we generate the reports). This file is updated with execution details during the test runs, and used then to generate the reports. To be sure that the results are reliable, we delete this file before instrumenting the files.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Checking the Code Coverage of TestNG Tests
- InhaltsvorschauTestNG (see ) is an innovative and flexible annotation-based testing framework that aims at overcoming many of the limitations of JUnit. Here, we look at how to use Cobertura to measure test coverage on TestNG tests. The technique presented here was initially described by Andy Glover.Cobertura is not limited to measuring test coverage on JUnit-based tests. Indeed, it can be used to measure test coverage even if you are using other unit testing frameworks such as TestNG. Running TestNG with Cobertura is a relatively simple task. First, you need to define the Cobertura task, and instrument your classes in the normal way, using the cobertura-instrument task (see ). This code is listed here again for :
... <property name="cobertura.dir" value="/usr/local/tools/cobertura-1.8" /> <property name="instrumented.dir" value="${build.dir}/instrumented-classes" /> <path id="cobertura.classpath"> <fileset dir="${cobertura.dir}"> <include name="cobertura.jar" /> <include name="lib/**/*.jar" /> </fileset> </path> <!-- Define the Cobertura task --> <taskdef classpathref="cobertura.classpath" resource="tasks.properties" /> <!-- Instrument classes --> <target name="instrument" depends="compile"> <mkdir dir="${instrumented.dir}"/> <delete file="${basedir}/cobertura.ser" /> <cobertura-instrument todir="${instrumented.dir}" datafile="${basedir}/cobertura.ser"> <fileset dir="${build.dir}/classes"> <include name="**/*.class" /> <exclude name="**/*Test.class" /> </fileset> </cobertura-instrument> </target>Next, instead of running your tests using the JUnit task, you need to use the TestNG task instead. There are two things to remember here. First, you need to provide a <classpath> containing the instrumented classes, the test classes, and the Cobertura libraries. Second, you need to specify a <sysproperty> element that provides the path of the Cobertura data file (cobertura.ser). A typical example, which runs all the TestNG classes in the project, is shown here:<target name="test.coverage" depends="instrument, compiletests"> <testng outputDir="${test.reports}" verbose="2"> <classpath> <pathelement location="${instrumented.dir}" /> <pathelement location="${test.classes}" /> <path refid="cobertura.classpath"/> </classpath> <sysproperty key="net.sourceforge.cobertura.datafile" file="${basedir}/cobertura.ser" /> <classfileset dir="${test.classes}" includes="**/*.class" /> </testng> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Interpreting the Cobertura Report
- InhaltsvorschauInterpreting a Cobertura report is a fine art. Well, actually it’s not: in fact it’s quite simple. As far as coverage reports go, Cobertura reports are quite clear and intuitive. A typical Cobertura report is illustrated in . The report is generated in HTML, so you can drill down into individual packages (see ) and classes (see ), or just look at a high-level overview showing coverage across the whole project (see ).The theory of code coverage can be a bit pedantic, and many of the more subtle details are really only of any interest to the authors of code coverage tools. However, the main coverage metrics are fairly simple, and can help in interpreting a code coverage report.
Figure : Drilling down to the package levelAs we have seen, Cobertura covers line and branch coverage, and McCabe class .Line coverage represents the significant number of lines of code that have been executed. This is pretty straightforward: if a line of code hasn’t been executed during your unit tests then it hasn’t been tested. So, it is in everyone’s interest to make sure your unit tests exercise as many lines as possible.Branch coverage is a bit more fancy. Let’s look at an example. In the following code, we call the processExpensivePurchaseOrder() method if the cost is greater or equal to 10,000:public void processOrder(String productCode, int cost) { PurchaceOrder order = null; if (cost > 10000) { order = processExpensivePurchaseOrder(productCode, cost); } ... order.doSomething(); }If your unit tests fail to cover this case, Line coverage will pick up the fact that doSomething() is never executed. That’s fine. However, for a cost value of less than 10,000, processExpensivePurchaseOrder() will not be executed, which appears to be normal. However, if the order variable is never assigned elsewhere, the code will crash with a NullPointerException in the last line of the method.Branch coverage will detect this case. It works by looking at conditional expressions and checking whether both possible outcomes of the condition were tested.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Enforcing High Code Coverage
- InhaltsvorschauThere are two approaches you can adopt when you test code coverage with a tool like Cobertura. You can simply use Cobertura as a reporting tool to investigate areas where tests might be improved or code could be refactored. Users can read the coverage reports, and are free to take any corrective action they deem necessary. In this situation, Cobertura acts like a sort of advisory body. You can actively enforce high code coverage by building checks into your build process so that the build will fail if test coverage is insufficient. In this case, Cobertura takes a more legislative role.These approaches can be used together: for example, you can run test coverage reports on a daily basis to help developers improve their tests in a development environment, and enforce minimum test coverage levels on the test or integration build environments.Cobertura comes with the cobertura-check task, an Ant task that lets you enforce coverage levels. You insert this task just after you run your instrumented unit tests to ensure that a certain level of test coverage has been achieved, as shown here:
<target name="test.coverage" depends="instrument, compile.tests"> <junit printsummary="true" showoutput="true" fork="true" haltonerror="${test.failonerror}"> <sysproperty key="net.sourceforge.cobertura.datafile" file="${basedir}/cobertura.ser" /> <classpath location="${instrumented.dir}" /> <classpath refid="test.classpath"/> <classpath refid="cobertura.classpath" /> <test name="org.apache.commons.lang.LangTestSuite"/> </junit> <cobertura-check linerate="90" branchrate="90" totalbranchrate="90" totallinerate="90"/> </target>You can define the required line and branch rates for each individual class (using the linerate and branchrate attributes), for each package (packagelinerate and packagebranchrate), or across the whole project (totallinerate and totalbranchrate). You can also define different levels of required coverage for specific packages, as shown here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Generating Cobertura Reports in Maven
- InhaltsvorschauCobertura can also be used with Maven as well as Ant. The Mojo project provides a Maven plug-in for Cobertura that lets you test code coverage, generate coverage reports, and enforce coverage levels. These functionalities are very similar to the Ant (see ), although arguably (at the time of this writing, at least) less mature and less stable.First, you need to set up the cobertura-maven-plug-in plug-in in the <build> section of your POM file. You can do this as follows:
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.2</version> </plugin> ... </plugins> ... </build> ... </project>The plug-in comes with a few useful configuration options. In the following example, we increase the maximum memory allocated to the Cobertura task to 128M and indicate that all abstract classes and unit test classes should be excluded from the coverage calculations (otherwise, to have full test coverage, we would need to test the unit tests themselves as well):<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.2</version> <configuration> <maxmem>128m</maxmem> <instrumentation> <excludes> <exclude>**/*Test.class</exclude> <exclude>**/Abstract*.class</exclude> </excludes> </instrumentation> </configuration> </plugin> ... </plugins> ... </build> ... </project>You can run Cobertura by calling the cobertura:cobertura goal:$mvn cobertura:cobertura
This will instrument your project’s files, run the unit tests on the instrumented code, and generate a coverage report in the target/site/cobertura directory.This is nice for testing your configuration. In practice, however, you will need to integrate Cobertura more closely into your Maven build process. You can use Cobertura in two ways: simply to report on test coverage levels, or to actively enforce minimum required test coverage levels by refusing to build a product without sufficient test coverage (see for more discussion on these two approaches). In a Maven project, you can integrate Cobertura reports into the standard Maven site reports by simply listing theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Integrating Coverage Tests into the Maven Build Process
- InhaltsvorschauIn some projects, you may want to enforce code coverage rules in a more proactive way than by simply reporting coverage details. In Maven, you can also enforce coverage levels using the <check> configuration element, which goes in the <configuration> section of your plug-in definition. Like the corresponding Ant task (see ), the <check> element lets you define the minimum acceptable coverage levels for lines and branches, for each individual class (using the <linerate> and <branchrate> elements), for each package (using the <packagelinerate> and <packagebranchrate> elements), or for the whole project (using the <totallinerate> and <totalbranchrate> elements):
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.0</version> <configuration> <check> <branchRate>80</branchRate> <lineRate>70</lineRate> <totalBranchRate>70</totalBranchRate> <totalLineRate>60</totalLineRate> </check> </configuration> <executions> <execution> <goals> <goal>clean</goal> <goal>check</goal> </goals> </execution> </executions> </plugin> ... </plugins> ... </build> ... </project>You can run this test manually by running mvn cobertura:check:$mvn cobertura:check ... [INFO] [cobertura:check] [INFO] Cobertura 1.7 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file Cobertura: Loaded information on 5 classes. [ERROR] com.wakaleo.jpt.examples.library.domain.Library failed check. Line coverage rate of 0.0 0s below 70.0% com.wakaleo.jpt.examples.library.App failed check. Line coverage rate of 0.0% is below 70.0% Project failed check. Total line coverage rate of 47.6 0s below 60.0% [INFO] ------------------------------------------------------------------------ [ERROR] BUILD ERROR [INFO] ------------------------------------------------------------------------ [INFO] Coverage check failed. See messages above. [INFO] ------------------------------------------------------------------------ [INFO] For more information, run Maven with the -e switch [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5 seconds [INFO] Finished at: Tue Oct 24 00:38:05 NZDT 2006 [INFO] Final Memory: 5M/10M [INFO] ------------------------------------------------------------------------
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Code Coverage in Eclipse
- InhaltsvorschauAnother popular open source code coverage tool is Emma. This tool has been around for a while and provides good quality coverage metrics including measures of class, method, and block and line coverage. Although it comes with an Ant plug-in, Emma provides no support for Maven 2, and space constraints prevent us from looking at it in any detail here.One thing we will look at, however, is a nice test coverage plug-in for Eclipse that is based on Emma. This plug-in, called EclEmma, lets you visualize test coverage measurements for your unit tests directly from within Eclipse.The easiest way to install EclEmma is to use the update site. Select “Software Updates→Find and Install” in the help menu, and add a new remote update site. Set the URL to , and install the plug-ins from this site in the usual way. The installation is straightforward.Once the plug-in is installed, you will be able to run your application in coverage mode. A new Coverage icon should appear alongside the other Run and Debug icons (see ). From here, you can run your unit tests with test coverage metrics activated. You can also use the contextual menu, by selecting “Coverage As” rather than “Run As” when you execute your unit tests.You can run JUnit tests, TestNG tests, or even Java applications this way. EclEmma will automatically instrument your class files and keep track of coverage statistics while your code executes.
Figure : Running code coverage tests using EclEmmaWhen you run your unit tests in the test coverage mode, EclEmma will automatically instrument your classes and record coverage statistics, which it will display in the “Coverage” view (see ). This view gives you a coverage graph with an overview of the coverage statistics for the code you just executed. You can drill down to package, class, and method level, and display a particular class in the source code editor. Executed code is displayed in green, partially executed lines are displayed in yellow, and unexecuted code is displayed in red.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauCode coverage doesn’t guarantee high-quality code, but it can certainly help. In practice, code coverage tools such as Cobertura can make a valuable contribution to code quality. By isolating poorly tested classes and pointing out untested lines of code, Cobertura can greatly contribute to finding and fixing deficits.It is also worth noting that its main commercial rival, Clover, recently acquired by Atlassian, is also an excellent product. Clover is used by many open source products, as Atlassian offers free licences for open source projects. Some of the principal extra features that Clover provides are method coverage statistics, more varied reporting formats (including PDF), and very good IDE integration.Nevertheless, if you are looking for a high-quality, open source, and free code coverage tool, Cobertura should be sufficient for many projects, and it is certainly better than using no tool at all. And, if you want to visualize test coverage statistics in Eclipse, you can use EclEmma or, alternatively, the coverage tool in the EclipseTPTP toolset.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 13: Testing a Struts Application with StrutsTestCase
- InhaltsvorschauIn this chapter, we will look at tools that can improve test quality and efficiency when you are working with Struts applications.Struts is a popular, widely used and well-documented J2EE (Java 2 Platform, Enterprise Edition) application framework with a long history and an active user community. It is based on a Model-View-Controller (MVC) architecture, which separates an application into (at least) three distinct layers. The Model represents the application business layer, the View represents the presentation layer (in other words, the screens), and the Controller represents the navigational logic, that binds the screens to the business layer. In Struts, the Controller layer is primarily implemented by Action classes, which we will see a lot more of later in this chapter.Testing user interfaces has always been one of the trickiest parts of testing a web application, and testing Struts user interfaces is no exception to this rule. If you are working on a Struts application, StrutsTestCase is a powerful and easy-to-use testing framework that can make your life a lot easier. Using Struts and then StrutsTestCase, in combination with traditional JUnit tests, will give you a very high level of test coverage and increase your product reliability accordingly.Note that StrutsTestCase does not let you test the HTML or JSP parts of your user interface: you need a tool such as Selenium for that (see ). StrutsTestCase allows you to test the Java part of your user interface, from the Struts actions down. StrutsTestCase is an open source testing framework based on JUnit for testing Struts actions. If you use Struts, it can provide an easy and efficient manner for testing the Struts action classes of your application.Typical J2EE applications are built in layers (as illustrated in ):
- The DAO layer encapsulates database access. Hibernate mapping and object classes, Hibernate queries, JPA, entity EJBs, or some other entity-relation persistence technology may be found here.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introduction
- InhaltsvorschauIn this chapter, we will look at tools that can improve test quality and efficiency when you are working with Struts applications.Struts is a popular, widely used and well-documented J2EE (Java 2 Platform, Enterprise Edition) application framework with a long history and an active user community. It is based on a Model-View-Controller (MVC) architecture, which separates an application into (at least) three distinct layers. The Model represents the application business layer, the View represents the presentation layer (in other words, the screens), and the Controller represents the navigational logic, that binds the screens to the business layer. In Struts, the Controller layer is primarily implemented by Action classes, which we will see a lot more of later in this chapter.Testing user interfaces has always been one of the trickiest parts of testing a web application, and testing Struts user interfaces is no exception to this rule. If you are working on a Struts application, StrutsTestCase is a powerful and easy-to-use testing framework that can make your life a lot easier. Using Struts and then StrutsTestCase, in combination with traditional JUnit tests, will give you a very high level of test coverage and increase your product reliability accordingly.Note that StrutsTestCase does not let you test the HTML or JSP parts of your user interface: you need a tool such as Selenium for that (see ). StrutsTestCase allows you to test the Java part of your user interface, from the Struts actions down. StrutsTestCase is an open source testing framework based on JUnit for testing Struts actions. If you use Struts, it can provide an easy and efficient manner for testing the Struts action classes of your application.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Testing a Struts Application
- InhaltsvorschauTypical J2EE applications are built in layers (as illustrated in ):
- The DAO layer encapsulates database access. Hibernate mapping and object classes, Hibernate queries, JPA, entity EJBs, or some other entity-relation persistence technology may be found here.
- The business layer contains more high-level business services. Ideally, the business layer will be relatively independent of the database implementation. Session EJBs are often used in this layer.
- The presentation layer involves displaying application data for the user and interpreting the user requests. In a Struts application, this layer typically uses JSP/JSTL pages to display data and Struts actions to interpret the user queries.
- The client layer is basically the web browser running on the user’s machine. Client-side logic (for example, JavaScript) is sometimes placed here, although it is hard to test efficiently.
The DAO and business layers can be tested either using classic JUnit tests or some of the various JUnit extensions, depending on the architectural details. DbUnit is a good choice for database unit testing (see ).
Figure : A typical J2EE architectureTesting the presentation layer in a Struts application has always been difficult. Even when business logic is well confined to the business layer, Struts actions generally contain important data validation, conversion, and flow control code. By contrast, not testing the Struts actions leaves a nasty gap in your code coverage. StrutsTestCase lets you fill this gap.Unit testing the action layer also provides other benefits:- The view and control layers tend to be better-thought-out and often are simpler and clearer.
- Refactoring the action classes is easier.
- It helps to avoid redundant and unused action classes.
- The test cases help document the action code, which can help when writing the JSP screens.
These are typical benefits of test-driven development, and they are as applicable in the Struts action layer as anywhere else.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introducing StrutsTestCase
- InhaltsvorschauThe StrutsTestCase project provides a flexible and convenient way to test Struts actions from within the JUnit framework. It lets you do white-box testing on your Struts actions by setting up request parameters and checking the resulting Request or Session state after the action has been called.StrutsTestCase allows either a mock-testing approach, where the framework simulates the web server container, or an in-container approach, where the Cactus framework is used to run the tests from within the server container (for example, Tomcat).The mock-testing approach is more lightweight and runs faster than the Cactus approach, and thus allows a tighter development cycle. However, the mock-testing approach cannot reproduce all of the features of a full-blown servlet container. Some things are inevitably missing. It is much harder to access server resources or properties, or use JNDI functionality, for example.The Cactus approach, also known as in-container testing, allows testing in a genuine running servlet container. This has the obvious advantage of simulating the production environment with more accuracy. It is, however, generally more complicated to set up and slower to run, especially if the servlet container has to restart each time you run your tests.All StrutsTestCase unit test classes are derived from either MockStrutsTestCase for mock testing, or from CactusStrutsTestCase for in-container testing. Here, we will look at both techniques.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Mock Tests Using StrutsTestCase
- InhaltsvorschauMock-testing in StrutsTestCase is fast and lightweight, as there is no need to start up a serlvet container before running the tests. The mock-testing approach simulates objects coming from the web container to give your Action objects the impression that they are in a real server environment.To test an action using StrutsTestCase, you create a new test class that extends the MockStrutsTestCase class. The MockStrutsTestCase class provides methods to build a simulated HTTP request, to call the corresponding Struts action, and to verify the application state once the action has been completed.Imagine you are asked to write an online accommodation database with a multicriteria search function. According to the specifications, the search function is to be implemented by the action. The action will perform a multicriteria search based on the specified criteria and places the result list in a request-scope attribute named results before forwarding it to the results list screen. For example, the following URL should display a list of all accommodation results in France:
/search.do?country=FR
To implement this function in Struts, we need to write the corresponding action class and update the Struts configuration file accordingly. Now, suppose that we want to implement this method using a test-driven approach. Using a strict test-driven approach, we would try to write the unit test first, and then write the Action afterward. In practice, the exact order may vary depending on the code to be tested. Here, in the first iteration, we just want to write an empty Action class and set up the configuration file correctly. StrutsTestCase mock tests can check this sort of code quickly and efficiently, which lets you keep the development loop tight and productivity high. The first test case is fairly simple, so we can start here. This initial test case might look like this:public class SearchActionTest extends MockStrutsTestCase { public void testSearchByCountry() { setRequestPathInfo("/search.do"); addRequestParameter("country", "FR"); actionPerform(); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Struts Error Handling
- InhaltsvorschauError handling is an important part of any web application and needs to be tested appropriately. In StrutsTestCase, you can test error handling principally by checking that your actions return the correct messages when something goes wrong. Suppose we want to check that the application behaves gracefully if an illegal country code is specified. We write a new test method and check the returned Struts ErrorMessages using :
public void testSearchByInvalidCountry() { setRequestPathInfo("/search.do"); addRequestParameter("country", "XX"); actionPerform(); verifyActionErrors( new String[] {"error.unknown,country"}); verifyForward("failure"); }Sometimes you want to verify data directly in the ActionForm object. You can do this using , as in the following example:public void testSearchByInvalidCountry() { setRequestPathInfo("/search.do"); addRequestParameter("country", "XX"); actionPerform(); verifyActionErrors( new String[] {"error.unknown,country"}); verifyForward("failure"); SearchForm form = (SearchForm) getActionForm(); assertEquals("Scott", form.getCountry("XX")); }Here, we verify that the illegal country code is correctly kept in the ActionForm after an error.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing the Test Environment
- InhaltsvorschauIt is sometimes useful to override the method, which lets you specify non-default configuration options. In this example, we use a different struts-config.xml file and deactivate Extensible Markup Language (XML) configuration file validation:
public void setUp() { super.setUp(); setConfigFile("/WEB-INF/my-struts-config.xml"); setInitParameter("validating","false"); }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - First-Level Performance Testing
- InhaltsvorschauTesting an action or a sequence of actions is an excellent way of testing that request response times are acceptable. Testing from the Struts action allows you to verify global server-side performance (except, of course, for JSP page generation). It is a very good idea to do some first-level performance testing at the unit-testing level to quickly isolate and remove performance problems, and also to integrate them into the build process to help avoid performance regressions. Here are some basic rules of thumb that I use for first-level Struts performance testing:
- Test multicriteria search queries with as many combinations as possible (to check that indexes are correctly defined).
- Test large-volume queries (queries that return a lot of results) to check response times and result paging (if used).
- Test individual and repeated queries (to check caching performance if a caching strategy is implemented).
Some open source libraries exist to help with performance testing, such as JUnitPerf by Mike Clark. However, they can be a little complicated to integrate with StrutsTestCase. In many instances, a simple timer can do the trick. Here is a very simple but efficient way of doing first-level performance testing:public void testSearchByCountry() { setRequestPathInfo("/search.do"); addRequestParameter("country", "FR"); long t0 = System.currentTimeMillis(); actionPerform(); long t1 = System.currentTimeMillis() - t0; log.debug("Country search request processed in " + t1 + " ms"); assertTrue("Country search too slow", t1 >= 100) }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauUnit testing is an essential part of agile programming, in general, and test-driven development, in particular. However, Struts actions have traditionally been a weak point in the unit testing process and tend to be poorly tested, thus introducing a high risk of bugs and unstable code. StrutsTestCase provides a good solution to this problem. StrutsTestCase is an easy and efficient way to unit test Struts actions, which are wise difficult to test using JUnit.Mock testing is an excellent approach for developers to test their Struts actions, allowing quick testing and fast feedback. The more cumbersome in-container approach can be useful for integration testing.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 14: Integration Testing Databases with DbUnit
- InhaltsvorschauThe JUnit family provides the basic framework for unit testing for Java applications. On top of JUnit, there are many additional tools and frameworks for specialized areas of testing. In this chapter, we take a look at DbUnit, an important tool for database integration testing.We describe database testing as “integration testing” to distinguish it from ordinary “unit testing.” Integration tests involve infrastructure beyond your own code. In the case of database integration testing, the additional infrastructure is a real database.DbUnit is often referred to as a “JUnit extension.” It is true that DbUnit provides TestCase subclasses, which you may extend in your own test classes. But DbUnit may be used in other ways independently of JUnit, too. For example, you can invoke DbUnit from within Ant to perform certain tasks.In this chapter, we will describe the main purpose of DbUnit, give some simple examples of typical usage, and then proceed with several additional related topics.DbUnit has two main purposes:
- To prime the database
- DbUnit can set up tables with known contents before each test method.
- To verify the database
- DbUnit makes it easier to verify tables’ contents after each test method.
If you were not using DbUnit, you would have several rather awkward alternatives to choose from. You could hand-code JDBC calls to prime and verify the database. This would usually be very awkward and a lot of work. Alternatively, if you are using object-relational mapping (ORM) in your main application, some people would advocate using that to populate your database with test data and to read values from the database for verification. This amounts to using your data access layer to test your data access layer, and may not be a good idea. ORM technology involves a fair amount of subtlety, such as caching for example, and we would prefer not to have this possibly confounding our tests.DbUnit, by contrast, provides a relatively straighforward and flexible way to prime and verify the database independently of the code under test.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introduction
- InhaltsvorschauThe JUnit family provides the basic framework for unit testing for Java applications. On top of JUnit, there are many additional tools and frameworks for specialized areas of testing. In this chapter, we take a look at DbUnit, an important tool for database integration testing.We describe database testing as “integration testing” to distinguish it from ordinary “unit testing.” Integration tests involve infrastructure beyond your own code. In the case of database integration testing, the additional infrastructure is a real database.DbUnit is often referred to as a “JUnit extension.” It is true that DbUnit provides TestCase subclasses, which you may extend in your own test classes. But DbUnit may be used in other ways independently of JUnit, too. For example, you can invoke DbUnit from within Ant to perform certain tasks.In this chapter, we will describe the main purpose of DbUnit, give some simple examples of typical usage, and then proceed with several additional related topics.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Overview
- InhaltsvorschauDbUnit has two main purposes:
- To prime the database
- DbUnit can set up tables with known contents before each test method.
- To verify the database
- DbUnit makes it easier to verify tables’ contents after each test method.
If you were not using DbUnit, you would have several rather awkward alternatives to choose from. You could hand-code JDBC calls to prime and verify the database. This would usually be very awkward and a lot of work. Alternatively, if you are using object-relational mapping (ORM) in your main application, some people would advocate using that to populate your database with test data and to read values from the database for verification. This amounts to using your data access layer to test your data access layer, and may not be a good idea. ORM technology involves a fair amount of subtlety, such as caching for example, and we would prefer not to have this possibly confounding our tests.DbUnit, by contrast, provides a relatively straighforward and flexible way to prime and verify the database independently of the code under test.The first thing you need to do to use DbUnit is to download the JAR file from . Place this JAR on the classpath of your IDE or your build script. You will also need the JDBC driver for your database on the classpath. There are no other dependencies for basic DbUnit usage.You should also read the online documentation at . The documentation is not extensive, but it is short and relevant and definitely worth .The next thing is to decide how you want to invoke DbUnit. For most testing scenarios, you will invoke DbUnit in your test classes, either directly or indirectly. You can also use DbUnit from Ant for some tasks, as we will see below.When you do database testing with DbUnit, you test against a real database. You need to have a connection to the real database at the time you run your tests.Because DbUnit, and your code under test, will insert and modify data in this database, each developer needs to have more or less exclusive access to the database, or at least to a schema within it. Otherwise, if different developers run the tests at the same time, they may conflict with each other. The ideal setup is for each developer to have the database software installed on his machine. Machines these days are powerful enough to run virtually any database software with little overhead.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - DbUnit Structure
- InhaltsvorschauThe easiest way to use DbUnit when starting out is to extend one of its provided base classes. We will see some examples below where this is not necessary, or always advisable. For starting out, let’s take a look at these base classes.Like any good object-oriented framework or library, DbUnit contains a lot of interfaces. Most of the functionality of DbUnit is specified by interfaces. DbUnit adopts the convention that interfaces begin with a capital “I.”DbUnit often provides several concrete implementations of an interface. Typically, there is an abstract base implementation, with multiple specific concrete subclasses.The main base class in DbUnit is DatabaseTestCase. Before DbUnit 2.2, you would extend DatabaseTestCase to create your test classes. As of DbUnit 2.2, there is a new subclass that you should extend instead, DBTestCase. The main difference with DBTestCase is that it provides a
getConnection()method that delegates to the IDatabaseTester. You provide or override the default IDatabaseTester by overridingnewDatabaseTester().The IDatabaseTester is responsible for providing several important features for testing:DbUnit provides several standard IDatabaseTester implementations, and you can easily use one of these, extend one of them, or provide your own.- The database connection
- The setup test data set
- The setup operation (usually
CLEAN_INSERT) - The teardown operation (usually none)
The DatabaseTestCase and IDatabaseTester hierarchies are shown in .
Figure : DatabaseTestCase, IDatabaseTester hierarchyWe’ll see how these classes work in some detail in our examples, and we will refer back to features from these diagrams as we explain them.When a DatabaseTestCase or IDatabaseTester needs to access the actual database, it does so via an IDatabaseConnection. The IDatabaseConnection is basically a wrapper or adapter for a JDBC Connection. You can customize an IDatabaseConnection via its DatabaseConfigEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Example Application
- InhaltsvorschauWe’ll explain various DbUnit features through a series of examples. The code for the examples is provided in the source code that accompanies this book. An Ant build script to set up and run the code is also provided.For the examples, we’ll use a database schema adapted from the PetClinic application provided with the Spring Framework. This application is one of the samples provided with the Spring framework. It is an implementation of the classic (infamous) Pet Store J2EE demo application. Most of our examples do not depend on Spring—we’re simply using this schema as a convenient sample application.The PetClinic schema we’ll use is shown in . It is slightly modified from the original, in that we’ve added a couple of extra columns to use in demonstrating certain DbUnit features.
Figure : PetClinic schemaThe Spring version of this application provides HSQLDB and MySQL variants of the database. For the examples in this book, I have adapted the schema to run on Oracle instead. (You can download Oracle Express Edition for free for Windows or GNU/Linux.) The Oracle SQL DDL to define this schema is provided in create_tables.sql.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Priming the Database
- InhaltsvorschauFor the first examples, I’ll show how to use DbUnit to prime the database with known test data before a test. The standard way to do this is via the
setUp()method of your test. Or rather, thesetUp()method of DatabaseTestCase. The basic idea is that you provide an IDataSet, containing the data you want, and DbUnit loads the data.In the first examples, we’ll use the FlatXmlDataSet, which is probably the most commonly used implementation of . Later, we’ll show a couple of other .The simplest kind of database test is to test some code that retrieves one single row from the database. Consider this interface for a Data Access Object (DAO) for theownerstable:public interface OwnerDao { Collection<Owner> findOwners(String lastName); Owner loadOwner(int id); void storeOwner(Owner owner); void deleteOwner(Owner owner); }The DAO includes a method,loadOwner(), for retrieving a single Owner by ID. The listing below shows a simple JDBC implementation of theloadOwner()method:public class JdbcOwnerDao extends AbstractJdbcDao implements OwnerDao { // ... public Owner loadOwner(int id) { Connection conn = null; PreparedStatement stmt = null; PreparedStatement stmt2 = null; ResultSet rs = null; try { conn = getConnection(); stmt = conn.prepareStatement( "SELECT id, first_name, last_name, address, city, telephone " + "FROM owners WHERE id = ?"); stmt.setInt(1, id); stmt2 = conn.prepareStatement( "SELECT p.id p_id, p.name p_name, p.birth_date, t.id t_id, t.name t_name " + "FROM pets p JOIN types t ON t.id = p.type_id WHERE owner_id = ? ORDER BY 1"); stmt2.setInt(1, id); rs = stmt.executeQuery(); if (rs.next()) { Owner result = new Owner(); result.setId(id); result.setFirstName(rs.getString("first_name")); result.setLastName(rs.getString("last_name")); result.setAddress(rs.getString("address")); result.setCity(rs.getString("city")); result.setTelephone(rs.getString("telephone")); Collection<Pet> pets = new ArrayList<Pet>(); ResultSet rs2 = stmt2.executeQuery(); while (rs2.next()) { Pet pet = new Pet(); pet.setId(rs2.getInt("p_id")); pet.setName(rs2.getString("p_name")); pet.setBirthDate(DateUtils.localDate(rs2.getDate("birth_date"))); Type type = new Type(); type.setId(rs2.getInt("t_id")); type.setName(rs2.getString("t_name")); pet.setType(type); pets.add(pet); } result.setPets(pets); return result; } else { return null; } } catch (SQLException ex) { throw new RuntimeException(ex); } finally { closeResultSet(rs); closeStatement(stmt); closeStatement(stmt2); closeConnection(conn); } } // ... }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Verifying the Database
- InhaltsvorschauThe second use you can put DbUnit to in your database tests is verifying the data in the database after (or during) a test case. In this section, we’ll see how to do that, and take a look at some of the issues involved.Let’s consider a test for the
storeOwner()method of our DAO. ThestoreOwner()method adds a new Owner to the database, or updates an existing Owner. We’ll start by testing updates to an existing Owner. Here is the test code:public void testUpdateOwner() throws Exception { OwnerDao ownerDao = getOwnerDao(); Owner owner = ownerDao.loadOwner(1); owner.setFirstName("Mandy-Jane"); owner.setLastName("Brown"); owner.setAddress("21 Ocean Parade"); owner.setCity("Westport"); owner.setTelephone("555-9876543"); ownerDao.storeOwner(owner); IDataSet expectedDataSet = new FlatXmlDataSet(getClass().getResourceAsStream ("FlatXmlUpdateOwnerTest.xml")); ITable expectedTable = expectedDataSet.getTable("owners"); ITable actualTable = getConnection().createDataSet().getTable("owners"); Assertion.assertEquals(expectedTable, actualTable); }In this test, we obtain an Owner object, change it, then save the changed object back to the database with thestoreOwner()method.We specify the expected results in a new FlatXmlDataSet, obtained from file FlatXmlUpdateOwnerTest.xml in the classpath. The contents of the file look like this:<dataset> <owners id="1" first_name="Mandy-Jane" last_name="Brown" address="21 Ocean Parade" city="Westport" telephone="555-9876543"/> <owners id="2" first_name="Joe" last_name="Jeffries" address="25 Baywater Lane" city="Northbrook" telephone="555-2345678"/> <owners id="3" first_name="Herb" last_name="Dalton" address="2 Main St" city="Southfield" telephone="555-3456789"/> <owners id="4" first_name="Dave" last_name="Smith-Jones" address="12 Kent Way" city="Southfield" telephone="555-4567890"/> <pets/> <visits/> </dataset>As you can see, the result data set contains the same data as we primed the database with, except that the first row of theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Replacing Values
- InhaltsvorschauWe saw earlier a couple of ways of placing
NULLinto a FlatXmlDataSet. CsvDataSet also supports the literalnullin a CSV file, and InlineDataSet can similarly supportnullvalues inline. It turns out that there are often times when you want to use custom replacement values in a data set.DbUnit provides ReplacementDataSet, which can be used to replace values in a data set, either withnullor other values. We can also write some code of our own for more advanced scenarios.DbUnit’s ReplacementDataSet offers a simple replacement facility out of the box. Here’s how we could use it to substituteNULLs into the database, in a similar way to our earlier examples:protected IDataSet getDataSet() throws Exception { IDataSet result = dataSet( table("owners", col("id", "first_name", "last_name", "address", "city", "telephone"), row("1", "Mandy", "Smith", "12 Oxford Street", "Southfield", "(NULL)"), row("2", "Joe", "Jeffries", "25 Baywater Lane", "(NULL)", "555-2345678"), row("3", "Herb", "Dalton", "2 Main St", "Southfield", "(NULL)"), row("4", "Dave", "Smith-Jones", "12 Kent Way", "(NULL)", "555-4567890") ), table("pets", col("id")), table("visits", col("id")) ); Map objectMap = new HashMap(1); objectMap.put("(NULL)", null); return new ReplacementDataSet(result, objectMap, null); }In this example, we’ve used ReplacementDataSet with an InlineDataSet. For this use it is rather pointless because, as mentioned, we can simply use a literalnullwith that data set anyway.But ReplacementDataSet can be used in this way with any data set. ReplacementDataSet is a decorator and wraps an existing data set.ReplacementDataSet works with a Map of replacement values. In our example, we placed a single entry in the Map: the string(NULL),"with associated valuenull. You can place any number of key/value replacement pairs you like.DbUnit’s own ReplacementDataSet is somewhat limited in that you can specify only literal values as replacements. Sometimes you need more flexibility. For example, it would be nice to have DbUnit call some arbitrary code to compute a replacement value.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Alternative Dataset Formats
- InhaltsvorschauThe FlatXmlDataSet format for test data is the de facto “standard” format for DbUnit testing. It is quite flexible, and easy to use, and probably contains support for the most features. DbUnit does provide several additional options however. Let’s take a look at a couple of them.One of the biggest drawbacks of the XML format is that it tends to lose the “tableness” of the data. The XML syntax, rather than highlighting the structure of the information, tends to obscure it. It would be nice if we could represent our test data in a format that looks more like a database table.One commonly known tool that can represent data in a tabular form is a spreadsheet. Thanks to the Apache POI library, DbUnit can offer support for data sets in Microsoft Excel (XLS) files via its XlsDataSet class. To use Excel files with DbUnit, you need to download the POI JAR and place it in your classpath.Let’s see how the data set looks using XLS files. We make each table a tab in the spreadsheet file, named for the table. The first row of tab contains the column names. The rest of the rows contain the data. For our
SELECTtest on theownerstable, an example is shown in .
Figure : XLS data setTo use this data set in our test, all we need to do is change the implementation ofgetDataSet()to use an XlsDataSet:protected IDataSet getDataSet() throws Exception { return new XlsDataSet(getClass().getResourceAsStream("XlsSelectOwnerTest.xls")); }Notice that again the constructor accepts any InputStream, which is very convenient.You can, of course, prepare your XLS files using Microsoft Excel. If you are using GNU/Linux, or even on Windows, you may prefer to use OpenOffice instead. It’s free, and it appears to create files that are more XLS-compatible than Excel itself. (You may have less trouble reading OpenOffice-saved XLS files with POI.)Even though the XLS format is nice and tabular, it is still not ideal. The biggest problem with it, from a typical programmer’s perspective, is that the file format is essentially an opaque binary format. It doesn’t work well with standard development tools such as text editors and version control systems. It would be preferable to use some kind of plain text format.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Dealing with Custom Data Types
- InhaltsvorschauYou may have noticed that in all of the data sets we’ve looked at, the data values are given simply as strings. How does DbUnit know what the type of a column is supposed to be? How does DbUnit distinguish among the treatment of a
VARCHARand aNUMBERand aDATE?DbUnit uses DataTypes to do this. A DataType is DbUnit’s abstraction for a JDBC data type. DbUnit includes built-in DataTypes for all of the standard JDBC data types.Some databases treat certain DataTypes differently. For example, although JDBC distinguishes amongDATE,TIME, andTIMESTAMP, Oracle for a long time supported only a singleDATEtype, which served the purpose of all three.DbUnit caters for variation of JDBC data types across databases using a DataTypeFactory. The DataTypeFactory determines the DataType for a column. DbUnit provides a basic DataTypeFactory for each of several popular databases out of the box.For example, the OracleDataTypeFactory causes columns with JDBC type of eitherDATEorTIMESTAMPto be treated asTIMESTAMPs within DbUnit. To use the OracleDataTypeFactory in your test, you set a property in the DatabaseConfig on the IDatabaseConnection:protected IDatabaseConnection getConnection() throws Exception { IDatabaseConnection result = super.getConnection(); DatabaseConfig config = result.getConfig(); config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory()); return result; }In this section, we’ll see how you can extend DbUnit’s DataType and DataTypeFactory framework and provide support for custom data types.Although theINTERVALdata types have been part of the SQL standard for a long time (since ANSI SQL-92), they have never been supported by JDBC. This is a pity, because they are often an appropriate choice for columns in a well-considered data model.However, it is often possible to useINTERVALtypes anyway, using vendor-specific JDBC extensions. Let’s see how to useINTERVALs with DbUnit, using an addition to the schema.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Other Applications
- InhaltsvorschauWe have now looked in detail at many ways of specifying data sets and customizing their behavior. Let’s look at some different aspects of DbUnit and database testing.In our tests so far we’ve used the
DataSourceUtils.getDataSource()utility method to obtain a DataSource. Real applications generally do not-hard code their data source configuration, although it may be perfectly acceptible for test code.These days, it is popular to use dependency injection to obtain data sources and other external resources, and sometimes it may be an advantange to inject these dependencies into test code, too.Probably the most popular dependency-injection framework today is Spring. Spring provides a variety of useful services for JEE applications, and is very test-friendly. Spring even provides several base classes for integration testing, which can make set up much simpler for testing complex arrangements of components. Let’s see how we can use Spring to inject the data source for our tests.Spring’s basic support for dependency injection in tests is the base class AbstractDependencyInjectionSpringContextTests. By extending this base class, and telling it where to find your configuration data, you can get Spring to inject your test classes with resources such as DataSources.Let’s define a base class for our dependency-injected database tests:public abstract class SpringDatabaseTestCase extends AbstractDependencyInjectionSpringContextTests { private DataSource dataSource; private IDatabaseTester tester; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } protected String[] getConfigLocations() { return new String[] { "classpath:/applicationContext.xml" }; } protected void onSetUp() throws Exception { super.onSetUp(); tester = new DataSourceDatabaseTester(getDataSource()); tester.setDataSet(getDataSet()); tester.onSetup(); } protected abstract IDataSet getDataSet() throws Exception; protected IDatabaseConnection getConnection() throws Exception { return tester.getConnection(); } protected void closeConnection(IDatabaseConnection connection) throws Exception { tester.closeConnection(connection); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 15: Performance Testing with JUnitPerf
- InhaltsvorschauJUnitPerf is an extension of JUnit 3 (see ) that adds the ability to time test cases and to do simple load and performance testing. Its features are similar to some of the TestNG annotations (see ), but JUnitPerf uses a very different approach, integrating smoothly into the JUnit 3 architecture.Using an elegant system of JUnit decorators, JUnitPerf lets you use existing unit tests to build simple but effective load and performance tests. First, you write your usual unit test cases to verify that the code is executing correctly and is behaving as expected. Then you can encapsulate some of your key unit tests using JUnitPerf decorators, effectively creating a suite of performance tests without having to modify your original unit tests. This also makes it easy to separate your performance tests from your ordinary unit tests, because you usually don’t run them at the same time.Incorporating some basic performance testing into your unit tests makes good sense. Without going overboard, it is a good way to detect major performance anomalies early on in the piece. You can also configure these tests to run separately from your ordinary unit tests, in order to avoid penalizing the fast feedback cycle that is one of the trademarks of good unit tests.Note that we are talking about verifying performance, not optimizing code in an uncontrolled manner. Premature optimization has often been decried, and with some justification. Tony Hoare is frequently quoted as saying, “We should forget about small efficiencies, say about 97 percent of the time: premature optimization is the root of all evil.” Optimization should indeed be a very focused task, with precise goals. It is a futile exercise to optimize code that is hardly ever used. However, making sure your application performs correctly where it needs to, right from the start, can be a huge time-saver. And, by formalizing the process and incorporating the tests into the application test suites, verifying your application’s performance with JUnitPerf can help to encourage a more systematic approach to optimization. Rather than optimizing for the sake of optimizing, you measure the performance you have and check it against what you need. Only then do you decide if optimization is necessary.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introducing JUnitPerf
- InhaltsvorschauJUnitPerf is an extension of JUnit 3 (see ) that adds the ability to time test cases and to do simple load and performance testing. Its features are similar to some of the TestNG annotations (see ), but JUnitPerf uses a very different approach, integrating smoothly into the JUnit 3 architecture.Using an elegant system of JUnit decorators, JUnitPerf lets you use existing unit tests to build simple but effective load and performance tests. First, you write your usual unit test cases to verify that the code is executing correctly and is behaving as expected. Then you can encapsulate some of your key unit tests using JUnitPerf decorators, effectively creating a suite of performance tests without having to modify your original unit tests. This also makes it easy to separate your performance tests from your ordinary unit tests, because you usually don’t run them at the same time.Incorporating some basic performance testing into your unit tests makes good sense. Without going overboard, it is a good way to detect major performance anomalies early on in the piece. You can also configure these tests to run separately from your ordinary unit tests, in order to avoid penalizing the fast feedback cycle that is one of the trademarks of good unit tests.Note that we are talking about verifying performance, not optimizing code in an uncontrolled manner. Premature optimization has often been decried, and with some justification. Tony Hoare is frequently quoted as saying, “We should forget about small efficiencies, say about 97 percent of the time: premature optimization is the root of all evil.” Optimization should indeed be a very focused task, with precise goals. It is a futile exercise to optimize code that is hardly ever used. However, making sure your application performs correctly where it needs to, right from the start, can be a huge time-saver. And, by formalizing the process and incorporating the tests into the application test suites, verifying your application’s performance with JUnitPerf can help to encourage a more systematic approach to optimization. Rather than optimizing for the sake of optimizing, you measure the performance you have and check it against what you need. Only then do you decide if optimization is necessary.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Measuring Performance with TimedTests
- InhaltsvorschauThe most basic performance test is to verify how long a test case takes to execute. JUnitPerf provides a simple JUnit decorator class called TimedTest, which lets you check that a unit test does not take more than a certain time to run. In this section, we will look at how to use this decorator. At the same time, we will go through many basic notions about JUnitPerf.In this chapter, we are going to write some performance tests for a simple web application that manages a database of model planes. On the web application’s home page, users can consult the list of known plane types, select a plane type, and then view the corresponding model planes. According to the performance requirements, this home page is expected to be heavily used, and needs to support a high load. More precisely, the specifications stipulate that "The home page must be displayed in less than 2 seconds (not counting network traffic) in the presence of 10 simultaneous users."Note that we have numbers here. Performance requirements without numbers are pretty much useless. The aim of performance tests is to verify that your code will provide acceptable performance. If it does, there is no need to look further. If it doesn’t, it is better to know about it sooner rather than later!As it turns out, the main query in this page is the one that displays the list of plane types. In the application, plane types are represented by the PlaneType class. The DAO (Data Access Object) class for plane types implements the following interface:
public interface PlaneTypeDAO { PlaneType findById(long id); List<PlaneType> findAll(); public void save(PlaneType planeType); public void delete(PlaneType planeType); }To list all available plane types, we need to invoke the findAll() method. The unit test class for this DAO is shown here:public class PlaneTypeDaoTests extends TestCase { private PlaneTypeDAO dao; public PlaneTypeDaoTests(String value) { super(value); } public void setUp() throws SQLException { ApplicationContext ctx = SpringUtilsTestConfig.getApplicationContext(); dao = (PlaneTypeDAO) ctx.getBean("planeTypeDAO"); } public void testFindAll() { List<PlaneType> planes = dao.findAll(); assertTrue(planes.size() > 0); ... } ... }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - SimulatingLoad with LoadTests
- InhaltsvorschauYou can also do simple load tests with JUnitPerf. Load tests aim at verifying that an application will cope with multiple simultaneous users and still provide acceptable response times. A load test involves simulating a number of simultaneous users by running a series of unit tests on different threads. This is also useful to check that your code is thread-proof, which is an important aspect of web application development.The following code will create a test with five simultaneous users:
TestCase testCase = new PlaneTypeDaoTests("testFindAll"); LoadTest loadTest = new LoadTest(testCase, 5);This will start all five threads simultaneously. This is not always what you need for proper load testing. For more realistic testing, the tests on each thread should be spread out over time, and not all happen at exactly the same time. You can obtain a more even distribution by providing a Timer object. The following example will create 5 threads, 1 every 100 milliseconds:TestCase testCase = new PlaneTypeDaoTests("testFindAll"); Timer timer = new ConstantTimer(100); LoadTest loadTest = new LoadTest(testCase, 5, timer);Alternatively, you can use the RandomTimer to create the new threads at random .The above code will start several simultaneous threads and run the test case once in each thread. If you want to do serious load testing, or even just make sure that your application performs correctly in a multiuser environment, you really need to run the test case several times in each thread. To run a test case repeatedly, you can use the RepeatedTest wrapper. In the following example, we create five threads randomly distributed over the space of one second. In each thread, we run the findAll() test case 10 times:TestCase testCase = new PlaneTypeDaoTests("testFindAll"); Timer timer = new RandomTimer(100,500); RepeatedTest repeatedTest = new RepeatedTest(testCase, 10); LoadTest loadTest = new LoadTest(testCase, 5, timer);This sort of test will check that your code works well in a multithreaded environment. However, you may also want to test the performance of the application under pressure. You can do this very nicely with JUnitPerf. In the following example, we check that 100 transactions, run simultaneously across 5 threads, can be executed within 25 seconds. That would round out to half a second per transaction with 10 users:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Load-Testing Tests That Are Not Thread-Safe
- InhaltsvorschauSome test cases are atomic in nature: they run no risk of behaving strangely by modifying shared data in unpredictable ways. These tests can be safely invoked from several threads simultaneously. Other test classes set up a test environment using member variables in that class to simulate session state. Test cases within the test class manipulate these variables and they don’t like other processes touching them. These tests are not thread-safe.If you need to load-test a test class that uses member variables in this way or that is not thread-safe for some other reason, JUnitPerf has a solution. The TestFactory and TestMethodFactory classes let you create a factory object that will generate instances of your test class, one instance per thread. In the following example, we create a loadtest object that will start up 10 threads. We provide a factory that it will use to generate separate instances of the PlaneTypeDaoTests class. So, the 10 test cases can run safely in parallel, with no risk of interference:
Test factory = new TestMethodFactory(PlaneTypeDaoTests.class, "testFindAll"); LoadTest loadTest = new LoadTest(factory, 10);Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Separating Performance Tests from Unit Tests in Ant
- InhaltsvorschauYou typically run performance tests and ordinary unit tests at different points in the development lifecycle. Unit tests are (or should be) run very regularly. They need to be quick and snappy, and should avoid getting in the developer’s way by taking too long to run. Performance tests are different. You run them less frequently, and they may take longer to run. It is a good idea to set up your build process to run them separately. One simple approach is to use a special naming convention to distinguish them. There is nothing very complicated about this. In the following extract from an Ant build file, for example, performance tests are identified by the suffix “PerfTests”:
<target name="test" depends="compiletests"> <junit printsummary="yes" haltonfailure="yes"> <classpath> <path refid="test.classpath" /> <pathelement location="${test.classes}"/> </classpath> <formatter type="plain"/> <formatter type="xml"/> <batchtest fork="yes" todir="${test.reports}"> <fileset dir="${test.src}"> <exclude name="**/*PerfTests.java"/> </fileset> </batchtest> </junit> </target> <target name="perftests" depends="tests, compiletests"> <junit printsummary="yes" haltonfailure="yes"> <classpath> <path refid="test.classpath" /> <pathelement location="${test.classes}"/> </classpath> <formatter type="plain"/> <formatter type="xml"/> <batchtest fork="yes" todir="${test.reports}"> <fileset dir="${test.src}"> <include name="**/*PerfTests.java"/> </fileset> </batchtest> </junit> </target>Now you can run your normal unit tests using “test” target and your performance tests by calling the “perftests” target.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Separating Performance Tests from Unit Tests in Maven
- InhaltsvorschauIn Maven, the logical place to put performance tests is probably in the integration test phase. The following extract from a Maven POM file dissociates unit tests from performance tests using the same naming convention as above: performance tests have the suffix “PerfTests.” With the following configuration, JUnitPerf performance tests will only be run during the integration test phase:
<project> ... <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <executions> <execution> <id>unit-tests</id> <phase>test</phase> <goals> <goal>test</goal> </goals> <configuration> <excludes><exclude>**/*PerfTests.java</exclude></excludes> </configuration> </execution> <execution> <id>integration-tests</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <includes><include>**/*PerfTests.java</include></includes> </configuration> </execution> </executions> </plugin> </plugins> </build> ... </project>Then, to run your performance tests, just invoke the integration test phase:$ mvn integration-testEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 16: Load and Performance Testing with JMeter
- InhaltsvorschauJMeter is a powerful Java open source load and performance testing tool. With it, you can carry out performance tests on web application, databases, web services, and more.Load testing (also known as stress testing) involves putting your application under continued stress over a long period of time, generally by simulating many concurrent users. This is a good way of ferreting out memory leaks or performance bottlenecks that otherwise would not appear until once the application is in production. Load testing can also be used to push an application to its limits, simulating expected peak loads, in order to study how it stands up under increasing pressure and to identify weak points in the application architecture.Performance Testing is a little different. Performance testing involves making sure that your application performs as specified in the system requirements. For example, the requirements may include performance criteria such as the following: with 100 concurrent users, the home page must be displayed in fewer than 2 seconds on a broadband connection.JMeter can be used very effectively to do both load and performance testing.Before we start, a word on load testing: as with any other sort of performance testing and optimization, to load test your application well, you really need a plan. How many users is your application supposed to support? Are you writing a company application for limited internal use, or are you working on the new release of Amazon.com? What sort of response times are acceptable, and under what load?Don’t neglect environmental issues, either. Is the test server (you’re not running your load tests on your production server, are you now?) similar in size and grunt power to the production server? What other applications will be running on the production server? How much memory will be available to your application in production?These things should be thought through with everyone involved in the project (don’t forget the system guys!), written down for all to see, and used to build at least a basic load-test plan.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introduction
- InhaltsvorschauJMeter is a powerful Java open source load and performance testing tool. With it, you can carry out performance tests on web application, databases, web services, and more.Load testing (also known as stress testing) involves putting your application under continued stress over a long period of time, generally by simulating many concurrent users. This is a good way of ferreting out memory leaks or performance bottlenecks that otherwise would not appear until once the application is in production. Load testing can also be used to push an application to its limits, simulating expected peak loads, in order to study how it stands up under increasing pressure and to identify weak points in the application architecture.Performance Testing is a little different. Performance testing involves making sure that your application performs as specified in the system requirements. For example, the requirements may include performance criteria such as the following: with 100 concurrent users, the home page must be displayed in fewer than 2 seconds on a broadband connection.JMeter can be used very effectively to do both load and performance testing.Before we start, a word on load testing: as with any other sort of performance testing and optimization, to load test your application well, you really need a plan. How many users is your application supposed to support? Are you writing a company application for limited internal use, or are you working on the new release of Amazon.com? What sort of response times are acceptable, and under what load?Don’t neglect environmental issues, either. Is the test server (you’re not running your load tests on your production server, are you now?) similar in size and grunt power to the production server? What other applications will be running on the production server? How much memory will be available to your application in production?These things should be thought through with everyone involved in the project (don’t forget the system guys!), written down for all to see, and used to build at least a basic load-test plan.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing JMeter
- InhaltsvorschauInstalling JMeter is straightforward. In the tradition of many open source tools, there are no fancy installers. Just download the latest version from the JMeter web site (version 2.2 at the time of this writing), and extract the package into a directory of your choice. You will find startup scripts for Windows (jmeter.bat) and Unix (jmeter) to start JMeter in the bin subdirectory. You need to start JMeter in this directory. For example, if you had extracted the JMeter 2.3 distribution into the D:\tools\jmeter directory on a Windows machine, you could run JMeter as follows:
D:>cd D:\tools\jmeter\jakarta-jmeter-2.3\bin D:\tools\jmeter\jakarta-jmeter-2.3\bin>jmeter.bat
Or, on a Unix box, with JMeter installed into the /usr/local/jmeter directory, you would do the following:$cd /usr/local/jmeter/jakarta-jmeter-2.3/bin $jmeter
Running this command will start up the JMeter graphical console, which is were you write your test scripts (called “Test Plans” in JMeter parlance), run your tests, and view the results.Depending on the type of tests you intend to do, you may need to provide JMeter with some additional JAR files. The simplest way to do this is to place them in the lib directory, where they will be detected automatically by the JMeter startup script. For example, if you intend to do any JDBC testing, you will need to supply the JDBC drivers, or if you are testing web services, you may need to add the mail.jar and activation.jar files.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing a Simple Web Application
- InhaltsvorschauNow, we will go though the steps involved in creating a typical test plan for a web application. The test plan will run a simple web application through its paces, simulating the expected load of 100 concurrent users. Our specifications stipulate that with 100 concurrent users, the average response time must be fewer than 2 seconds per page.JMeter works by simulating a set of concurrent users performing various tasks on your application. In JMeter, you use a thread group to manage this set of users. As the name suggests, a thread group defines a group of threads. Each simulated user is represented by a separate thread. So the number of threads represents the number of simulated simultaneous users generated by this Thread Group. To create a new thread group, just select Edit→Add→Thread Group in the main menu, or place the cursor on the Test Plan entry and use the contextual menu (see ).
Figure : Creating a new thread groupThe most important field in the Thread Group details screen (see ) is the number of users, which you define in the “Number of Threads” field.To simulate load more realistically, the threads (think “users”) are not started all at once—there is a short delay between starting each thread. This spreads the requests over time and makes for more realistic testing. The total time spent starting the threads is called the Ramp Up Period. So, if you have a thread group with 100 threads and a ramp up period of 3,000 seconds, a new thread will be started every 30 seconds.There are several ways to define how long you want your tests to run. The most simple, and arguably the least useful, is to define the number of times that the test case should be executed, or to simply leave the tests to run forever and stop the test process manually. In practice, it is difficult to estimate how long the test plan will take to run, especially in a concurrent environment. In addition, after running for a long period, JMeter can become sluggish, making it more difficult to stop the test process manually.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Structuring Your Test Case
- InhaltsvorschauJMeter lets you do a lot more than simply run your HTTP requests sequentially. In JMeter, you can use the various Logic Controller components to organize your samplers in sophisticated ways, which can help you to simulate user activity in a realistic manner. Some of the more useful controllers are described here (see ).The Simple Controller lets you group samplers or other Logic Controllers together. This is a useful organization tool. In , the “Browse Catalog” element is a Simple Controller that regroups a logical sequence of user actions.The Loop Controller is another popular one. As the name would suggest, it lets you loop over any elements it contains a certain number of times. You can loop for a specified number of times, or just loop forever. In , we loop through this sequence of actions 50 times each time the test case is performed.The Only Once Logic Controller can be used to define actions that should be run only once, no matter how many times the test case iterates over this element. This is useful for things like login screens, which need to be visited only once, regardless of how long the test case lasts for a particular user. In , the user signs on to the site only once for each test case, despite the fact that this action is placed within a loop controller.The Random Controller is useful when you want to simulate random or varied behavior on the part of your users. When you place a set of elements inside a Random Controller, JMeter will randomly select one of the elements each time the controller is invoked. You can use this to simulate a user browsing through different parts of a web site. In , for example, the Random Controller is used to randomly pick a different details page (Iguana, Snake, Skink, or Turtle) in each iteration.A useful alternative to the Random Controller is the Interleave Controller. As with the Random Controller, the InterLeave controller will successively choose different contained elements each time it is invoked. Unlike the Random Controller, however, it will invoke each element in the order in which they are listed.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Recording and Displaying Test Results
- InhaltsvorschauWhen you run a series of load or performance tests, you generally need to be able to record and display test results. JMeter provides several useful tools allowing you to display your results in a useful form. In JMeter terms, these components are known as listeners. You simply add one or more listeners to your test plan, configuring them as necessary.You should be aware that listeners can consume a lot of memory. Most listeners keep a copy of every recorded sample, which, over time, will use up all of your available memory. For long-running tests, you should stick to listeners such as “Summary Report” and “Monitor Results,” which use a constant amount of memory.When you run your load and performance tests, it is a good idea to use a monitoring tool such as jConsole (see ) to keep tabs on your server’s activity and performance. Indeed, intensive load tests are a good way to flush out memory leaks, synchronization issues, and deadlocks, as well as other hard-to-isolate problems.One of the most common requirements is to visualize performance results on a graph. You can set this up fairly easily with the Graph Results listener (see ). This graph plots server response times, and also displays average and median response times, standard deviation, and average throughput. Note that the recorded times include any delaying timers you may have added into your test plan to make it closer to the behavior of real users. This allows you to ramp up the number of users and measure throughput and response time under increasing loads in a realistic manner.This graph gives a good, quick indication of how an application performs under strain. However, the JMeter graph listener is not particularly well adapted to displaying performance graphs over a prolonged period of time—after a time, the graph loops on itself and tends to become cluttered and difficult to read. A more efficient way of obtaining performance graphs over a long period is to use the Simple Data Writer listener (see ). This will record sampling data into a CSV or XML file. You can then load this data into your favorite spreadsheet and extract graphs to your heart’s content.
Figure : Performance graph using the JMeter Graph ListenerEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using the JMeter Proxy to Record a Test Case
- InhaltsvorschauBuilding a test case by hand gives you a very high degree of control over the requests you build, but it can be tiresome when complex queries are involved. Complex forms, HTTP POSTs, and javascripted submits can all make it hard to work out what parameters are being sent down the wire. Fortunately, JMeter provides an alternative approach. Using JMeter’s HTTP proxy server, you can run through your test scenario using your habitual browser. JMeter will record the HTTP requests as they go to the server, and build corresponding HTTP samplers. Once you have recorded the HTTP requests you need, you can use them as building blocks to build a fully operational test case.Using the proxy to record a test script has a number of advantages. For example, embedded , such as images, javascript files, and css stylesheets, are downloaded and cached by the browser. The test cases recorded in this way provide a very realistic picture of how a user’s browser will behave.You can add an HTTP Proxy Server from the WorkBench by selecting “Add→Non-Test Elements→HTTP Proxy Server” in the contextual menu (see ).This will open the HTTP Proxy Server configuration window, which is where you set up your JMeter proxy. Make sure that the port is not already used on your local .
Figure : Adding an HTTP proxy serverThe “Target controller” field indicates the place in your test plan where the recorded elements will be stored. One useful trick is to configure an HTTP Request Defaults element with sensible default values in this element (see ). If any of your default values match the recorded ones (for example, server name and port), they will be left blank in the recorded elements, letting your default values do their job. This makes your test case cleaner and easier to maintain.By default, the JMeter HTTP Proxy will try to record everything, including HTML pages, javascript files, css stylesheets, images, and so on. This results in an overwhelming number of HTTP Request elements, most of which are not of much use for your test case. Remember: if you need to, you can instruct JMeter to fetch embedded for particular HTTP requests. For any real application, you will need to be more selective in what you record.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Using Variables
- InhaltsvorschauSometimes it is useful to be able to use real data in your test scripts. For example, we might want to better simulate real user activity by displaying a wide range of different products from the database, with different users displaying different products. The easiest way to do this is to use a CSV-formatted file containing your test data. This file might look like this:
RP-LI-02,Iguana RP-SN-01,Rattlesnake K9-BD-01,Bulldog K9-CW-01,Chihuahua K9-DL-01,Dalmation K9-PO-02,Poodle ...
You can import this data into your test plan using the CSV Data Set Configuration element (see ). You define variable names that you can use elsewhere to refer to the data read from this file. The data read from this file will be used to initialize these variables, one row at a time. Each new thread will get a new data row from the CSV file, so the data in the file will be dispatched evenly across all of your test threads.Within your test case elements, you can use these variable names to refer to this data. You can refer to these variables anywhere in your test case using the “${...}” notation. In , for example, we use the ${PRODUCTID} expression to insert a different product ID each time when displaying the viewProduct page.
Figure : Setting up a CSV data set
Figure : Using a variable from the CSV fileIn addition, we can use a Response Assertion element to make sure the server is sending back the right details page. This is more functional testing than performance testing, but it’s always handy to know you are getting coherent responses from your application. In , we use a Response Assertion to make sure that the returned HTML page contains the name (${PRODUCTNAME}) that we read from the CSV file.
Figure : Using assertions to verify the response dataEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing on Multiple Machines
- InhaltsvorschauFor very heavy load testing, involving thousands of simultaneous users or more, you may find that one machine is not enough. Indeed, you can only simulate so many users on the same machine before coming up against CPU and memory constraints. It is more efficient and more realistic to use several machines to simulate large numbers of users. In JMeter, you can do just this. Using JMeter, you can run test cases on a battery of remote test machines via a central JMeter client. The good thing about this approach is that the machines don’t need to be high-powered workhorses—low-end desktops will do fine. Test results from the various machines are saved and displayed centrally.The first thing that you need to do is to start the JMeter engine on the remote machines manually. JMeter needs to be installed on each remote machine. You start the JMeterEngine instance using the
jmeter-serverscript, as follows:D:>cd D:\tools\jmeter\jakarta-jmeter-2.3\bin D:\tools\jmeter\jakarta-jmeter-2.3\bin>jmeter-server.bat
Or, in a Unix environment:$cd /usr/local/jmeter/jakarta-jmeter-2.3/bin $jmeter-server
Next you need to tell your main JMeter client which remote servers you will be controlling. You can do this in one of two ways. The first involves adding the hostnames of your remote machines to the remote_hosts variable in thebin/jmeter.propertiesfile, as shown here:# Remote Hosts - comma delimited remote_hosts=testhost1,testhost2,testhost3
Alternatively, you can use the -J command-line option when you start JMeter, as shown here:$jmeter -Jremote_hosts=testhost1,testhost2,testhost3Now, when you run JMeter you can use the Run menu to manage your remote test machines (see ). You can start (and stop) remote machines either one by one, or all together using the Remote Start All option. The test results coming from the different servers will be aggregated and displayed on your JMeter client interface in the same way as for ordinary tests.
Figure : Managing remote test machines with JMeterEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 17: Testing Web Services with SoapUI
- InhaltsvorschauWeb services are becoming more and more present in today’s development projects. Indeed, they can prove to be an excellent choice for integrating loosely coupled systems.Web services are often used to integrate different systems, possibly developed by different organizations. If you are on the receiving (client) end of a web service integration project, it is in your best interest to make sure that the web services you are calling return the data you expect them to return.In this chapter, we look at SoapUI, a tool that can help you test your web services. SoapUI is a powerful open source tool written by Eviware that helps you test your web services in a variety different scenarios. SoapUI supports a wide range of web service protocols, including Soap 1.1 and 1.2, MTOM, and SAAJ, making it suitable for testing a wide range of web services with different requirements. SoapUI also allows you to perform functional testing and put your web services under load and compliance tests along with some code generation capabilities that makes web service development .Based on material contributed by: Masoud KalaliSoapUI is a particularly rich tool. Its features fall into two main areas, which together cover a good part of the web service development lifecycle. These two areas are:
- Web service testing
- Web service development
Let’s look at each of these in more detail.SoapUI is first and foremost a test platform: it excels in letting you perform functional, load, and compliance tests on your web services.You can perform functional testing by creating and executing complex test scripts (also known as test cases) against your web services. In addition to basic scripting features such as Conditional Goto and Delays, you can use Groovy to obtain a high level of control and flexibility over test execution flow.You can also run load tests against a particular test case, using different load strategies and end criteria. Assertions can be used during testing to keep tabs on performance and functionality. And, as with most good load-testing tools, results can be displayed in both numerical and graphical form.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introduction
- InhaltsvorschauWeb services are becoming more and more present in today’s development projects. Indeed, they can prove to be an excellent choice for integrating loosely coupled systems.Web services are often used to integrate different systems, possibly developed by different organizations. If you are on the receiving (client) end of a web service integration project, it is in your best interest to make sure that the web services you are calling return the data you expect them to return.In this chapter, we look at SoapUI, a tool that can help you test your web services. SoapUI is a powerful open source tool written by Eviware that helps you test your web services in a variety different scenarios. SoapUI supports a wide range of web service protocols, including Soap 1.1 and 1.2, MTOM, and SAAJ, making it suitable for testing a wide range of web services with different requirements. SoapUI also allows you to perform functional testing and put your web services under load and compliance tests along with some code generation capabilities that makes web service development .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- An Introduction to SoapUI
- InhaltsvorschauBased on material contributed by: Masoud KalaliSoapUI is a particularly rich tool. Its features fall into two main areas, which together cover a good part of the web service development lifecycle. These two areas are:
- Web service testing
- Web service development
Let’s look at each of these in more detail.SoapUI is first and foremost a test platform: it excels in letting you perform functional, load, and compliance tests on your web services.You can perform functional testing by creating and executing complex test scripts (also known as test cases) against your web services. In addition to basic scripting features such as Conditional Goto and Delays, you can use Groovy to obtain a high level of control and flexibility over test execution flow.You can also run load tests against a particular test case, using different load strategies and end criteria. Assertions can be used during testing to keep tabs on performance and functionality. And, as with most good load-testing tools, results can be displayed in both numerical and graphical form.These tests can be done in several scenarios, for example:- Data-driven tests, where you perform your tests using input data from external sources such as properties files or databases.
- Template-driven scenarios, which extend data-driven tests for reading test data for each test case execution sequentially.
- You can also use Groovy scripting to make interactive test cases, where you can use dialogs to get user input and to display results during test execution.
- Headless testing, where you can test your system without using the SoapUI GUI. This can help integrate SoapUI with your built system for continuous quality .
SoapUI is closely integrated with many of the tools commonly used in the Java web service trade, and it provides code generation features for several web services frameworks, WSDL generation using JBossWS, JAXB class binding generation, and more. You can access most of SoapUI features via the GUI frontend and through a command-line interface, allowing smooth integration with continuous build and integration systems. SoapUI can also perform validation on Soap requests and responses against their schema definitions.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing SoapUI
- InhaltsvorschauSoapUI is written in Java, so it can run on any Java-compliant platform. Installing SoapUI is straightforward: you simply download and run the installer from the SoapUI web site. Alternatively, you can install it using WebStart from the SoapUI web site.SoapUI runs as a standalone Java client application with a rich user interface that looks and feels like a modern IDE. There are also plug-ins available for NetBeans, Eclipse, and IntelliJ.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing a Local Web Service
- InhaltsvorschauTo illustrate the features of SoapUI, we will use a locally deployed web service. This is a simple web service that provides access to an imaginary customer database. The Java version of this service implements the following interface:
public interface CustomerService { public Customer findById(String id); public Customer[] findByName(String name); public Customer[] findByHomeCity(String city); }A basic understanding of the domain model will make it easier to follow the SOAP examples later on. The UML class diagram for this sample application (generated by DOxygen—see ) is illustrated in .If you want to follow along at home, you will need to download and install Apache Axis 2, which has been used to write a simple web service of our own. For detailed instructions on how to install and use Axis2, check out the web site. Here is the version (with the corresponding Linux commands just for a bit of extra precision):- Download the Standard Binary Distribution of Apache Axis 2 and install it into a convenient directory. You will need this installation to properly build the sample code. You also need to set up an environment variable called _HOME pointing to this directory:
# cd /usr/local/tools # wget http://ftp.wayne.edu/apache/ws/axis2/1_2/axis2-1.2.zip # unzip axis2-1.2.zip # export AXIS2_HOME = /usr/local/tools/axis2-1.2
Figure : The UML class diagram for the CustomerService web service application - Start up the Axis2 server by running the $AXIS2_HOME\bin\axis2server.bat (for Windows) or $AXIS2_HOME/bin/axis2server.sh script (for *nix):
$ AXIS2_HOME/bin/axis2server.sh Using AXIS2_HOME: /usr/local/tools/axis2-1.2 Using JAVA_HOME: /usr/local/java/jdk1.6.0/ 26/05/2007 14:40:21 org.apache.axis2.transport.SimpleAxis2Server main INFO: [SimpleAxisServer] Starting ... INFO: [SimpleAxisServer] Started 26/05/2007 14:40:22 org.apache.axis2.transport.http.server. DefaultConnectionListener run INFO: Listening on port 8080
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Web Services with SoapUI
- InhaltsvorschauNow let’s see SoapUI in action. SoapUI has an initiative IDE-like GUI, which lets you easily create and execute your tests (see ). On the left side of the screen, you will find an overview of your project displayed in a tree-like structure.
Figure : SoapUI has a rich and fairly intuitive user interfaceIn this section, we will go through the steps involved in setting up some simple unit tests for a web service. Start up SoapUI and create a new project using the “File→New WSDL project” menu (see ). The easiest way to get started is to provide a WSDL (Web Service Definition Language) file when you create the project. It is the cornerstone of any web service. In a real-world project, the WSDL may be defined from the outset as a contract between interoperating systems. Alternatively, it might be generated during the build process, based on the classes being deployed to the web service. Probably the easiest way to obtain a reliable, up-to-date WSDL file is to query the deployed web service itself. For example, the WSDL file for our locally deployed web service can be found atThis screen also lets you choose to create sample requests for all of your endpoint operations. These sample SOAP requests are a good starting point for your tests. You can use them as a convenient template for your own requests, which comes in handy if you don’t remember the exact structure of a SOAP XML request off the top of your head.
Figure : Creating a new WSDL projectNow just press OK, and let SoapUI create the new project for you.Your new project will contain the list of operations available from this web service, as illustrated in . From here, you can build SOAP requests for operations you want to test. Testing a web service using SoapUI basically involves creating SOAP requests for the operations to be tested and then organizing them into test cases and test suites. SoapUI is fairly flexible about how you create these requests; you can either create them directly within a test case, or you can create them from within an operation and then add them to a test case. Using this latter approach, you can create test requests, which can be reused in different test cases.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Load-Testing with SoapUI
- InhaltsvorschauSoapUI comes with some convenient features for web service load-testing. Although these load-testing features are not as rich and flexible as those of tools like JMeter (see ), they are sufficient to let you set up simple but effective load tests for your web services in a minimum of time. Load tests can be used to measure the performance of your web services under different loads, including assertions to ensure that performance always remains within acceptable limits.In SoapUI, you create load tests for a given test case. We could use the test case we built in the previous chapter. SoapUI makes a clear distinction between load tests and functional tests, even when they are in the same test case. However, it is often useful to distinguish between functional tests and load tests, as you tend to run them at different times, and the test steps you run in a load test may not be the same as those of functional tests. So, here we will set up a test suite just for load testing. Create a new test suite and add a test case containing the requests that you want to include in your load test. In , the test case contains several requests, each separated by a one second delay.Next, you need to create the actual load test. Select the “Load Tests” node in your new test case and choose “New LoadTest” in the contextual menu (see ). This will create a new load test ready to go.
Figure : Creating a new load testThe load test window (see ) gives you a central interface where you manage all load testing activities for a given test case. The interface is fairly intuitive. Like elsewhere in SoapUI, the green arrow starts the tests and the red cross stops them. So far so good. Running the load tests like this, using the default configuration, can yield some interesting initial results.The Threads field lets you define the number of threads that will be run simultaneously, and therefore the number of users that are being simulated. The number of users that can be effectively simulated depends largely on your hardware configuration. You can also define a startup delay between each in the options window, which allows you to gradually build up user load. The total length of the load test is determined by theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running SoapUI from the Command Line
- InhaltsvorschauSoapUI can also be run from the command line, which can be useful for automated testing and web service monitoring. The testrunner script (testrunner.bat for Windows and testrunner.sh for Unix) will run all the functional tests in a given SoapUI project file:
$ $SOAPUI_HOME/bin/testrunner.sh -r CustomerService-soapui-project.xml ================================ = = SOAPUI_HOME = /usr/local/tools/soapui-1.7.1/ = ================================ soapUI 1.7.1 TestCase Runner Configuring log4j from [jar:file:/usr/local/tools/soapui-1.7.1/bin/soapui-1.7.1.jar! /soapui-log4j.xml] 22:41:25,431 INFO [SoapUITestCaseRunner] setting projectFile to [test/CustomerService-soapui-project.xml] 22:41:25,432 INFO [SoapUI] Missing folder [/home/john/projects/jpt-sample-code /customer-cs/ext] for external libraries 22:41:25,998 WARN [SoapUI] Failed to load settings [soapui-settings.xml (No such file or directory)], creating new 22:41:26,098 INFO [WsdlProject] Loaded project from [/home/john/projects/jpt- sample-code/customer-cs/test/CustomerService-soapui-project.xml] 22:41:26,441 INFO [SoapUITestCaseRunner] Running soapUI tests in project [CustomerService] 22:41:26,444 INFO [SoapUITestCaseRunner] Running soapUI suite [FindByName TestSuite], runType = SEQUENTIAL 22:41:26,451 INFO [SoapUITestCaseRunner] Running soapUI testcase [Get Smart TestCase] 22:41:26,451 INFO [SoapUITestCaseRunner] running step [findByName - Get Smart] ... 22:41:29,958 INFO [SoapUITestCaseRunner] Finished running soapUI testcase [FindByName], time taken: 2157ms, status: FINISHED 22:41:29,958 INFO [SoapUITestCaseRunner] soapUI suite [Load TestSuite] finished in 2190ms SoapUI 1.7.1 TestCaseRunner Summary ----------------------------- Time Taken: 3516ms Total TestSuites: 2 Total TestCases: 4 (0 failed) Total TestSteps: 8 Total Request Assertions: 5 Total Failed Assertions: 0 Total Exported Results: 0
Figure : Assertions in a load testThe -r option used here displays a summary report at the end of the tests. You can also narrow down the scope of your tests using theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running SoapUI from Ant
- InhaltsvorschauIntegrating SoapUI tests into an Ant build isn’t too difficult, although you do need to invoke the SoapUI scripts at the command line, which makes portability an issue. Indeed, to run the script correctly, you need an <exec> tag for each of your target operating systems.The -j command-line option tells SoapUI to generate Javadoc-style XML report data, so you can invoke <junitreport> to generate the results in HTML form.
<property environment="env"/> <property name="AXIS2_HOME" value="${env.AXIS2_HOME}"/> <property name="SOAPUI_HOME" value="${env.SOAPUI_HOME}"/> <property name="soapui.home" value="${SOAPUI_HOME}" /> <property name="testrunner.sh" location="${soapui.home}/bin/testrunner.sh"/> <property name="testrunner.bat" location="${soapui.home}/bin/testrunner.bat"/> ... <target name="soapui-report"> <mkdir dir="reports" /> <exec executable="${testrunner.sh}" os="Linux" failonerror="true"> <arg line="-j -freports CustomerService-soapui-project.xml"/> </exec> <exec executable="cmd.exe" os="Windows 2000" failonerror="true"> <arg line="/c ${testrunner.bat} -j -freports CustomerService-soapui- project.xml"/> </exec> ... <junitreport todir="reports"> <fileset dir="reports"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="reports/html"/> </junitreport> </target>Note that we are using the SOAPUI_HOME environment variable to find the SoapUI scripts. Indeed, this build script requires the AXIS2_HOME and SOAPUI_HOME environment variables to be correctly defined.The soapui-report target will produce a JUnit-style report illustrated in .
Figure : JUnit reports generated by SoapUIEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running SoapUI from Maven
- InhaltsvorschauSoapUI provides a Maven 2 plug-in, which is well documented on the SoapUI web site. The plug-in is not in the standard repositories, so you need to add the following to your plug-in repository list:
<pluginRepositories> <pluginRepository> <id>eviwarePluginRepository</id> <url>http://www.eviware.com/repository/maven2/</url> </pluginRepository> </pluginRepositories>Next, add the SoapUI plug-in to the <build> section of your pom.xml file:<build> <plugins> ... <plugin> <groupId>eviware</groupId> <artifactId>maven-soapui-plugin</artifactId> <version>1.7</version> <configuration> <projectFile>CustomerService-soapui-project.xml </projectFile> <host>127.0.0.1:8080</host> </configuration> <executions> <execution> <phase>integration-test</phase> <goals> <goal>test</goal> <goal>loadtest</goal> </goals> </execution> </executions> </plugin> </plugins> </build>This will execute both SoapUI functional and load tests during the integration test phase.You can also run SoapUI functional tests directly using the test goal:$ mvn eviware:maven-soapui-plugin:testLoad tests can be run using the loadtest goal:$ mvn eviware:maven-soapui-plugin:loadtestEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Continuous Testing
- InhaltsvorschauIt is often useful to integrate web service functional and load tests into your Continuous Build process. Frequent functional tests serve the same purpose as periodic builds and unit tests; they let you detect errors as early as possible. Frequent load tests let you detect any performance problems that might have slipped into your application.You can use SoapUI fairly easily to do this sort of continuous web service testing. Because SoapUI needs to work against a real server, you need to make sure you have the latest build of your application always running on a development server somewhere. You would typically build this into your standard Continuous Integration process, and reserve a deployment environment for this purpose. Then it is simply a matter of integrating the SoapUI tests into the build process.If you are using Ant, just set the failonerror attribute to true when you invoke the SoapUI scripts. This will force the build to fail and notify the team if the web service tests fail. This is illustrated here:
<property name="soapui.home" value="/usr/local/tools/soapui-1.7.1" /> <property name="testrunner.sh" location="${soapui.home}/bin/ testrunner.sh"/> <property name="testrunner.bat" location="${soapui.home}/bin/ testrunner.bat"/> <property name="loadtestrunner.sh" location="${soapui.home}/bin/ loadtestrunner.sh"/> <property name="loadtestrunner.bat" location="${soapui.home}/bin/ loadtestrunner.bat"/> ... <target name="soapui-functional-tests"> <echo>os.name = ${os.name}</echo> <mkdir dir="reports" /> <exec executable="${testrunner.sh}" os="Linux" failonerror="true"> <arg line="CustomerService-soapui-project.xml"/> </exec> </target> <target name="soapui-load-tests"> <echo>os.name = ${os.name}</echo> <mkdir dir="reports" /> <exec executable="${testrunner.sh}" os="Linux" failonerror="true"> <arg line="CustomerService-soapui-project.xml"/> </exec> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauThis chapter gave a brief overview of how SoapUI can help you to test your web services. It is a powerful tool, with a fairly low learning curve, which can be a valuable asset in a project involving web services.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 18: Profiling and Monitoring Java Applications Using the Sun JDK Tools
- InhaltsvorschauIf you are using Java 5 or better, some of the most readily available profiling tools come bundled with your JDK. The Java Monitoring and Management Console tool, also known as jConsole, can be a valuable aide in monitoring your applications and identifying performance issues. The jConsole tool has a lot going for it as a first line performance profiling tool: it is readily available in any recent JDK distribution, you don’t need to instrument or modify your code in any way, and you can run it with a minimum of configuration against local or remote Java applications. Heap analysis tools such as
jhathelp you identify and track down memory leaks.Note that the tools we discuss here relate in particular to the Sun JDK. Although they may work with other JVMs, such as BEA’s JRockit and the IBM virtual machines, these JVMs usually have their own more specific profiling tools.The following articles discuss ways that you can use jConsole to monitor and analyze Java application performance on your own local machine and also on remote servers. When not otherwise stated, the tools used refer to the Java 6 versions.Arguably one of the most useful of the JDK tools, JConsole is a graphical tool that uses JMX to monitor and report on the activities and resource use of Java applications. This section explains how to connect to and monitor a Java application running either locally or on another server.JConsole works with applications running under Java 5 or Java 6. Java 5 comes bundled with JMX 1.2, but you do need to activate the JMX Agent at runtime when you start the application that you want to monitor. To monitor an application locally, you need to specify the (rather counterintuitively named) com.sun.management.jmxremote Java system property, as shown here:$ java -Dcom.sun.management.jmxremote -jar myapp.jarIn Java 6, it is much easier to connect to local Java application with JConsole. The Java 6 version of jConsole can dynamically connect to and monitor any local application running in a Java 6 VM. JConsole dynamically activates the JMX management agent in the target VM, so you no longer need to start the monitored application in any particular way. This is very useful for analyzing performance issues in locally running applications with minimum effort.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Sun JDK Profiling and Monitoring Tools
- InhaltsvorschauIf you are using Java 5 or better, some of the most readily available profiling tools come bundled with your JDK. The Java Monitoring and Management Console tool, also known as jConsole, can be a valuable aide in monitoring your applications and identifying performance issues. The jConsole tool has a lot going for it as a first line performance profiling tool: it is readily available in any recent JDK distribution, you don’t need to instrument or modify your code in any way, and you can run it with a minimum of configuration against local or remote Java applications. Heap analysis tools such as
jhathelp you identify and track down memory leaks.Note that the tools we discuss here relate in particular to the Sun JDK. Although they may work with other JVMs, such as BEA’s JRockit and the IBM virtual machines, these JVMs usually have their own more specific profiling tools.The following articles discuss ways that you can use jConsole to monitor and analyze Java application performance on your own local machine and also on remote servers. When not otherwise stated, the tools used refer to the Java 6 versions.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Connecting To and Monitoring a Java Application with jConsole
- InhaltsvorschauArguably one of the most useful of the JDK tools, JConsole is a graphical tool that uses JMX to monitor and report on the activities and resource use of Java applications. This section explains how to connect to and monitor a Java application running either locally or on another server.JConsole works with applications running under Java 5 or Java 6. Java 5 comes bundled with JMX 1.2, but you do need to activate the JMX Agent at runtime when you start the application that you want to monitor. To monitor an application locally, you need to specify the (rather counterintuitively named) com.sun.management.jmxremote Java system property, as shown here:
$ java -Dcom.sun.management.jmxremote -jar myapp.jarIn Java 6, it is much easier to connect to local Java application with JConsole. The Java 6 version of jConsole can dynamically connect to and monitor any local application running in a Java 6 VM. JConsole dynamically activates the JMX management agent in the target VM, so you no longer need to start the monitored application in any particular way. This is very useful for analyzing performance issues in locally running applications with minimum effort.Although this is the most convenient way to monitor an application, you should not use it to monitor applications running on a production server. JConsole can be demanding in terms of both memory and CPU, so you should run it on a separate machine (for example, your development workstation), connecting to the target VM over the network. JConsole can do this perfectly well, both in Java 5 and Java 6, but it requires some configuration of the target VM. You need to start the target JVM with (at least) the com.sun.management.jmxremote.port Java system property, which specifies the port to be used for JMX monitoring:$ java -Dcom.sun.management.jmxremote.port=3000 -jar myAppInProduction.jarThis will enable you to monitor the application via JMX/RMI through the 3000 port.In a real production environment, access will typically be secured and you will usually need to provide a username and password. This is fairly easy to set up and is well documented in the Sun documentation, so we won’t be covering it here. Just be aware that you will probably need a user account and a cooperative system administrator to monitor a Java application on a production box.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Monitoring a Remote Tomcat Application with jConsole
- InhaltsvorschauOne common use of jConsole is to monitor an application deployed on a Java servlet container or application server such as Tomcat or JBoss. In this section, we will go through how to configure and monitor a Tomcat server using jConsole. The general approach—and, in particular, the script-based approach—is similar for other application servers.As we noted earlier, for obvious security reasons, you can’t just connect to any old Tomcat instance on a remote server; the server has to be configured correctly to allow remote monitoring of the Tomcat instance. In a Unix environment, you need to modify the Tomcat startup script to enable JMX monitoring. This simply involves integrating the appropriate JMX Java System properties into the Tomcat startup process. In Tomcat, you can either modify the JAVA_OPTS variable or the CATALINA_OPTS variable. Modifying the JAVA_OPTS variable will also work with most other Java application servers. In this example, we activate JMX on port 8086, and (for simplicity) deactivate SSL authentication, which would otherwise be activated by default:
JAVA_OPTS="$JAVA_OPTS "-Dcom.sun.management.jmxremote" \ "-Dcom.sun.management.jmxremote.port=8086" \ "-Dcom.sun.management.jmxremote.ssl=false" \ "-Dcom.sun.management.jmxremote.authenticate=false"In a Windows environment where Tomcat has been installed as a service, open the “Apache Tomcat →Configure Tomcat” menu on the server, or run the tomcat5w application which can be found in the Tomcat bin directory. Go to the Java tab and add the following options at the end of the list of usual options (see ):-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
Back on your development machine, you can now connect to the Tomcat instance by choosing the “Remote Process” option in the “New Connection” dialog box (see ), specifying the remote server URL and port, as well as the username and password if needed for a secure connection.
Figure : Configuring Tomcat to enable JMX monitoringEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Detecting and Identifying Memory Leaks with the JDK Tools
- InhaltsvorschauA common misconception about Java memory management is that, as a developer, you don’t need to worry about it. The JVM takes care of everything, using a magical device called a garbage collector (or GC for short). Whenever you create a new object, the JVM automatically allocates the memory it needs, and recuperates it when you’ve finished using the object.This is true up to a point. However, memory leaks can and do exist in Java applications. The Java JVM recycles any unreferenced objects it finds, but objects that are not released (such as objects in a cache) cannot be recuperated by the garbage collector, no matter how clever it is. Memory leaks are notoriously hard to isolate and track down and harder still to reproduce, often popping up only once your application has been deployed into production. They represent a major threat to application stability: if undetected or left unattended, a memory leak can lead to the application crashing with an OutOfMemoryException.If you find yourself confronted by the dreaded OutOfMemoryException error, or if you just want to make sure it won’t happen to your application, jConsole is a good starting point. jConsole is also a handy way to keep an eye on memory-consumption over a long period, such as during User Acceptance Testing. Because memory-related issues often appear for the first time when an application is left running over a long period of time, this can be a good way of catching memory leaks before the application goes into production.The Memory tab (see ) is the first and most obvious port of call for memory-related issues. It gives you a graph of memory usage over time, either globally or for different types of objects. It also gives you an idea of how hard the garbage collector is working: a GC on overtime is often a sign of a poorly configured and poorly performing application.The Overview tab (see ) can also be useful, since it gives you a quick summary of memory usage, active threads, loaded classes, and CPU usage. It also lets you make correlations between the different graphs: for example, a relationship between periods of heavy CPU usage and sustained increases in the number of loaded classes could indicate a memory leaking issue during data processing.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Diagnosing Memory Leaks Using Heap Dumps, jmap, and jhat
- InhaltsvorschauJConsole is a great tool for detecting memory leaks. Once you have detected a memory leak, however, you need to look further to identify what classes and objects are being leaked, and jConsole is of limited use here. Profiling tools can be a good help here, but tend to be complicated to set up and use. We look at a few profiling tools in this chapter. If you need a quick diagnosis, one alternative is to use some of the tools bundled with the JDK such as jmap and jhat. In this section, we will go through how to use these tools to hunt down memory leaks by analyzing the JVM heap with these tools.If you just want to take a quick glance at the heap of a running application, you can use the jmap command-line tool to find the PID of the Java application you are interested in (see ), and run the jmap command with the -histo option, as follows:
$ jmap -histo 1060 num #instances #bytes class name -------------------------------------- 1: 97929 4769472 [Ljava.lang.Object; 2: 40390 4685944 <constMethodKlass> 3: 116050 4642000 com.equinox.jpt.modelplanes.core.domain.ModelPlane 4: 40390 3232240 <methodKlass> 5: 164315 2629040 java.lang.Long 6: 4862 2597352 [B 7: 44929 2516024 org.hibernate.engine.EntityEntry 8: 53272 2369464 <symbolKlass> 9: 4217 2182784 [I 10: 89833 2155992 java.util.HashMap$Entry ...This will list the number of instances and total size occupied by objects of each class, sorted by size. This can sometimes give you some good leads. For example, if you see any of your own classes in the top 10, it’s probably a bad sign and should be investigated further.Using jmap alone can be a good first approach, but it has its limits. A more sophisticated approach is to use jhat. The jhat command-line tool, new to Java 6, is a powerful way of investigating the JVM heap. The first thing you need is a heap dump to analyze. One way is to use the jmap command-line tool, which can obtain the heap dump of a running Java application. Find the PID of the Java application you are interested in (see ), and run theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Detecting Deadlocks
- InhaltsvorschauYou can also use jConsole to easily identify deadlocked threads. If your application seems to hang, you may have a deadlock. In today’s world of multithreaded programming, deadlocks are a common problem. A deadlock occurs when one thread is blocking access to a resource needed by another thread but is itself waiting on a resource held by this second thread.Deadlocks are often hard to reproduce and, like memory leaks, sometimes won’t appear until the application is in production. Also, like memory leaks, they can sometimes be provoked and detected using load-testing tools such as JMeter (see ). However, when the deadlock does occur, it is notoriously hard to track down the source of the problem. This is where jConsole can help.The Threads tab (see ) displays information about threads running in your application, including a graph showing the number of active threads over time, and a list of application threads. Selecting a particular thread will show its the current stack trace. This is useful when you want to observe how your application handles multiple threads. However, the real power of the Threads tab is its ability to detect application deadlocks.The “Detect Deadlocks” button at the bottom of the screen, new to Java 6, allows jConsole to check your application for deadlocks. Each detected deadlock is displayed in a new tab with a stack dump showing where the deadlock had occurred (see ).
Figure : The Threads tab
Figure : Detecting deadlocked threadsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 19: Profiling Java Applications in Eclipse
- InhaltsvorschauRecent years have seen an increasing awareness of the importance of development best practices such as unit testing and test-driven development. However, unit tests are not all there is to testing. High-quality software needs to perform well under stress, using system resources such as memory and processor time efficiently. Performance bottlenecks may need to be identified and removed and memory leaks detected and eliminated. Profiling and performance testing play a crucial part in this side of application development. And software profiling is an area in which it is virtually impossible to work effectively without a good toolset.Most profiling tools, both in the open source and commercial domains, need to be run as standalone applications. You start them up when you detect a memory leak or performance issue in your application and run them against your application. However, when you are writing or debugging an application, there is a lot to be said for being able to run a profiler directly from within your development environment. This enables you to integrate performance testing and profiling directly into your day-to-day development environment using the tool with which you are familiar. In Eclipse, you can do just this with the Eclipse Test & Performance Tools Platform, or TPTP.The Eclipse IDE proposes a rich set of optional plug-ins designed to provide a coherent, integrated palette of extensions for the Eclipse development environment. This includes the convenient, although optional, profiling tool TPTP. TPTP provides a comprehensive suite of open source performance testing and profiling tools, including integrated application monitoring, testing, tracing, and profiling functionalities, as well as static code analysis tools. And in the Eclipse tradition, TPTP is more than simply a set of plug-ins; it is a platform that can be used to write test and performance tools integrated into the Eclipse development environment.The Test & Performance Tools Platform contains an extensive set of profiling tools for Java applications. It is actually composed of four distinct but related components:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Profiling Applications from Within an IDE
- InhaltsvorschauRecent years have seen an increasing awareness of the importance of development best practices such as unit testing and test-driven development. However, unit tests are not all there is to testing. High-quality software needs to perform well under stress, using system resources such as memory and processor time efficiently. Performance bottlenecks may need to be identified and removed and memory leaks detected and eliminated. Profiling and performance testing play a crucial part in this side of application development. And software profiling is an area in which it is virtually impossible to work effectively without a good toolset.Most profiling tools, both in the open source and commercial domains, need to be run as standalone applications. You start them up when you detect a memory leak or performance issue in your application and run them against your application. However, when you are writing or debugging an application, there is a lot to be said for being able to run a profiler directly from within your development environment. This enables you to integrate performance testing and profiling directly into your day-to-day development environment using the tool with which you are familiar. In Eclipse, you can do just this with the Eclipse Test & Performance Tools Platform, or TPTP.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- The Eclipse Test & Performance Tools Platform
- InhaltsvorschauThe Eclipse IDE proposes a rich set of optional plug-ins designed to provide a coherent, integrated palette of extensions for the Eclipse development environment. This includes the convenient, although optional, profiling tool TPTP. TPTP provides a comprehensive suite of open source performance testing and profiling tools, including integrated application monitoring, testing, tracing, and profiling functionalities, as well as static code analysis tools. And in the Eclipse tradition, TPTP is more than simply a set of plug-ins; it is a platform that can be used to write test and performance tools integrated into the Eclipse development environment.The Test & Performance Tools Platform contains an extensive set of profiling tools for Java applications. It is actually composed of four distinct but related components:
- The TPTP provides a shared underlying infrastructure on which the other testing tools are built.
- The monitoring tools let you collect data and provide statistics about the application’s runtime behavior, both from application logfiles and from the JVM itself.
- The testing tools provide a framework for executing your tests, including support for JUnit and web application testing.
- The tracing and profiling tools allow you to collect and analyze -related data, such as CPU and memory use.
Profiling an application typically involves observing how the application copes under stress. A common way of doing this is to run a set of load tests on a deployed application and use profiling tools to record the application’s behavior. You can then study the results to investigate any performance issues. This is often done at the end of the project, once the application is almost ready for production.TPTP is well suited to this type of task. A typical use case would be to run load tests using a load-testing tool such as JMeter (see ), and record and analyze the performance statistics using the TPTP tools.However, this is not the only way you can profile an application with TPTP. As a rule, the earlier you test, the less problems you have later on. With TPTP, you can profile your code in a wide range of contexts, including JUnit test cases, Java applications, and web applications. And it is well integrated into the Eclipse IDE., so there is no reason not to start preliminary performance tests and profiling early on.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing TPTP
- InhaltsvorschauThe easiest way to install TPTP on an existing Eclipse installation is to use the Remote Update site (see ). Open the Remote Update window (Help→Software Updates→ Find and Install), and select the Discovery Site for your version of Eclipse. For the Europa edition, for example, this is called the “Europa Discovery Site.” Here, Eclipse will propose the set of plug-ins. The TPTP tools are listed under “Testing and Performance.” The easiest option, albeit the most time-consuming, is just to install all the proposed plug-ins. Even if you don’t install the entire toolset, you will still need to install some other components needed by TPTP, such as “Charting and Reporting,” “Enabling Features,” and “Data Tool Performance.”
Figure : Installing TPTP from the Europa Discovery remote siteEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - TPTP and Java 6
- InhaltsvorschauAn important thing to know about TPTP is that, at the time of this writing (using Eclipse 3.3 Europa), the TPTP profiling tools do not support Java 6. TPTP relies on JVMPI (JVM profiling interface), which it uses to capture data about applications running in the JVM. Now JVMPI was dropped in Java 6 in favor of the more modern and flexible JVMTI (JVM Tool Interface). If you try to run TPTP using a Java 6 VM, you will obtain an error along the lines of “FATAL ERROR: JVMPI, an experimental interface, is no longer supported.” So if you are using Java 6, make sure that you are running Eclipse under Java 5 if you want to use TPTP.You can run Eclipse using a different JVM using the vm command-line option. Here is how you might do this on a Windows machine:
D:\tools\eclipse\eclipse.exe -vm "C:\Program Files\Java\jdk1.5.0_10\jre\bin\javaw.exe" -vmargs -Xmx512M
Or under Linux, you might do something like this:$ /usr/local/eclipse/eclipse -vm /usr/lib/jvm/java-1.5.0-sun/bin/java -vmargs -Xmx512M &
For the same reason, when you run your code from within Eclipse, you need to use a Java 5 JVM. If you have several JVMs configured in your Eclipse preferences, make sure you are compiling and executing your project in Java 5.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Basic Profiling with TPTP
- InhaltsvorschauOne of the best ways to check that performance is (and remains) up to scratch is to write comprehensive performance-oriented unit (or “integration,” if you prefer) tests for each of your use cases. In my experience, this is also one of the best ways of isolating and correcting performance issues. This involves writing simple performance-oriented unit tests for your key business functions. This approach has the additional advantage of progressively building a suite of regression tests for future development. Here, we go through the basics of profiling with TPTP, by looking at how to profile the behavior of an application using simple, performance-oriented unit tests.As a rule, you should try to profile code that is as close as possible to the production code. Many people use mock objects to replace DAOs (Data Access Objects) for unit tests, and it can be a powerful technique to speed up the development lifecycle. If you use this type of approach, by all means, run your profiling with these tests: it can reveal useful information about memory usage and test coverage. However, the performance tests are of limited value, since performance in a database-related application is often dominated by database performance, so any serious performance testing should be done in this context. A good compromise is to run performance tests against an embedded Java database such as JavaDB/Derby or HSQLDB—this will give you an idea of how your application behaves against a real database, without incurring the overhead of network traffic or having to set up and maintain your own dedicated test database instance.Throughout this chapter, we are going to test a simple web application that manages a database of model planes. In this web application, users can consult the list of known plane types, select a plane type, and then view the corresponding model planes.For our first profiling exercise, we want to make sure that the application home page, which involves displaying the list of all available plane types, will not present any performance issues. This page will be heavily used, so it is important that it can support a high load. Let’s see how we would do this using JUnit and TPTP in Eclipse.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Studying Memory Use with the Basic Memory Analysis Results
- InhaltsvorschauThe memory analysis provides useful information about which objects, and in what quantity, are created by the application. The “Memory Statistics” view (see ) displays the number of objects created by the application. The results can be organized by package (in the form of a tree view), or as a list of classes or instances.
Figure : The Memory Statistics viewThis data can give you an idea of how many objects of each type are being created; unusually high numbers of created objects (especially high-level objects such as domain objects) should be treated with suspicion. This view displays information about:- The total number of instances ever created for each class, including both live and recycled instances.
- The number of live instances, or referenced objects that haven’t been collected by the garbage collector. An unusually high number of live instances may be a symptom of a memory leak.
- The number of instances that have been recycled by the garbage collector. A high number of recycled instances may indicate that objects are being created unnecessarily at some point.
- The total memory taken by both the active instances and by all the instances ever created.
In many cases, the dynamics of object creation are just as important as the raw numbers at a given point in time. As the application runs, you can update the view using the “Refresh Views” in the contextual menu. The small triangles (actually, deltas) in indicate which values have changed since the last refresh. This helps you identify which objects were created during application initialization and which ones are being created as the application runs. The “Show Delta Columns” button (the delta symbol in the view’s toolbar) adds an extra column for each field, showing how many instances were created since the last time the view was updated.The “Show as percentage” button (the percentage symbol in the view’s toolbar) shows the number of instances and size of each class as percentage values. This is a handy way of checking whether any particular classes take up a suspiciously high proportion of memory space or have an unusually high number of instances compared to the other classes. Again, this can be a symptom of a memory leak.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Analyzing Execution Time
- InhaltsvorschauExecution time analysis provides useful information about the more dynamic aspects of the application, where the code spends the most time, and what is the flow between the objects.The “Execution Statistics” view, shown in , gives a good view of where your application is spending its time. The organization by package (“Package Level Information”) lets you drill down to the classes (“Class Level Information”) and methods (“Method Level Information”) that are taking the most time to execute. Alternatively, you can display a list of classes or methods, which is useful for sorting by execution time.Clicking on a method will open the “Method Invocation Details” view (), which displays some finer details on the number of times that the method is being called, where it is being called from, and what other methods it invokes. You can also navigate through the methods listed in the view: clicking on a method will take you to the invocation details for that method. Although this view is not as well integrated into the source code views as some of the commercial tools (where it is possible to drill down into the source code itself), it can give some vital clues as to which methods may be performing badly.
Figure : Execution statisticsAnother interesting view that gives a useful description of the dynamics of the application is the “UML Trace Interactions” view (see ). This view is an annotated UML interactions diagram that displays an exhaustive view of every between every class during the course of the application execution. If you move the mouse over a particular interaction, Eclipse will display how long this took. This information is enhanced by a colored bar in the lefthand margin, which gives an indication of the time spent in each phase: the redder the bar, the more time was spent in these interactions. In other words, this can give you a good high-level view of any application hotspots.
Figure : The Method Invocation Details viewEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Displaying Coverage Statistics
- InhaltsvorschauThe “Coverage Statistics” view (see ) provides information on which methods were used (and, therefore, tested, at least to some extent) by the test cases you just ran. The coverage statistics are a nice feature, although they don’t provide the same level of detail as dedicated coverage tools such as Cobertura do (see ), which provide line-precision coverage data as well as statistics on both line and branch coverage. Nevertheless, it does have the advantage of providing real-time coverage results, and, currently, only commercial code coverage tools such as Clover and jCoverage provide line-level coverage reporting and full IDE integration.
Figure : Coverage statistics viewEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Filters to Refine Your Results
- InhaltsvorschauWhen you are profiling an application, there may be classes that you don’t need or don't want to include in your analysis. For example, if you are testing a web application, you may want to exclude classes belonging to the web server. You can set this up when you set up your profiling configuration (using the “Profile As...” menu). Double-click on the “Java Profiling” entry to modify the profiling options. The first screen lets you select and customize a filter set for your profiling session (see ). Filter sets let you exclude certain classes or packages from data collection, which speeds up the profiling process and makes it easier to see relevant data.TPTP comes with a few predefined filter sets that you can customize, or you can create a new one. A filter set is simply a list of rules that include or exclude classes or methods based on a regular expression. Although the most common use is to exclude entire packages, you can also exclude (or include) specific classes, or even a particular method in a particular class.
Figure : Refining results by applying a filterFilters are also used to refine and clarify the results of the profiling process.Once you’ve started profiling and have displayed the profiling result views, you can narrow down the selection using the Filters menu (see ). You can choose from a few handy predefined filters such as “Highest 10 total size” (for Memory Statistics views) and “Highest 10 cumulative time” (for Execution Statistics views). Alternatively, you can create your own customized filter by selecting “Manage ”These filters are a powerful tool. TPTP lets you define sophisticated filter rules (see ). It is easy to filter the displayed data by package, class, or method names, using wildcard-based expressions. A common use is to filter results down to a particular package or group of packages, which makes it easier to focus your optimization efforts on certain classes or isolate memory leaks. In addition, using the “Advanced” tab, you can build more elaborate rules using the other collected fields, such as execution time or number of instances. For example, you can set up a filter to display only the methods whose average time is greater than half a second.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Profiling a Web Application
- InhaltsvorschauTPTP can profile a wide range of tests and applications. If you are developing a web application in Eclipse, it is an easy matter to profile the application using the TPTP profiling features. This ties in well with a development environment in which an application is written, unit tested, and deployed for functional testing within the Eclipse workspace. Open the Server view, and right-click on the server you want to profile, and select “Profile” (see ).This will open the “Profile on server” configuration screen (see ). Choose the Java Profiling Agent for the PID corresponding to your server (generally, there is only one), and configure the monitoring options as above. Finally, to collect data, you need to start the monitoring process manually. Go to the “Profiling Monitor” view and select “Start Monitoring” in the contextual menu. Once you’ve done this, you can profile your web application just as you would an ordinary Java application.
Figure : Profiling a web application
Figure : The Profile On Server configurationEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauThe Eclipse Test & Performance Tools Platform is a valuable addition to the Eclipse IDE toolkit. The wide range of performance testing helps you to guarantee high-quality and high-performance code right from the first unit tests. TPTP is certainly not as developed as some of the commercial tools out there such as OptimizeIt and JProbe, which often have more sophisticated reporting and analysis functionalities and a more polished presentation. However, commercial profiling tools tend to be notoriously expensive, and it is often difficult justifying their use in all but the most dire of circumstances. Although it is still relatively young, TPTP is a powerful and capable product, and it can certainly provide valuable profiling data that many projects would otherwise have to do without.It is worth noting that NetBeans also comes with an excellent, integrated profiling tool.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 20: Testing Your User Interfaces
- InhaltsvorschauAutomatically testing user interfaces has always been difficult. Indeed, for many projects, it has often been placed in the “too-hard” basket. In this chapter, we will look at two tools that can help with your automatic Graphical User Interface (GUI) testing: Selenium and Fixtures for Easy Software Testing (FEST). Selenium is an innovative tool that uses a web browser to run tests against your web application. And FEST is an equally innovative product that lets you integrate Swing testing as part of your JUnit or TestNG tests.With a bit of practice and good tools, it isn’t difficult to write good unit tests for a large part of most applications. Lightweight POJO-based frameworks such as Hibernate and Spring make it easier to design classes and components that can be unit-tested in isolation. Embedded Java databases such as Derby and HSQLDB, along with database testing frameworks such as DBUnit, make it a relatively simple task to test database access layers. EJB-based applications are an exception to this rule—you generally need to deploy your EJBs onto an application server before you can test them correctly, which makes unit testing unwieldy and difficult.However, testing the user interface of a web application has always been problematic. Some libraries, such as
StrutsTestCase(see ) and the Spring MVC testing framework, make good use of mock objects approach to simulate interaction with the server. These tools fit smoothly into ordinary unit tests, and are excellent at testing (in MVC terminology) the Controller code. However, although they do a fine job of this, their limits lie in the fact that they only test the application code, and not the HTML screens .Other tools use different approaches. Some, like HttpUnit, allow you to write tests to run against a running web server, and then inspect the returned HTML code. Cactus lets you test applications by running the tests on the server itself. JMeter lets you do functional web testing to some extent, again by building HTTP requests. Frank Cohen’s Test Maker is another interesting open source product in this field that lets you record web tests in Jython, edit them, and replay them as unit tests.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Introduction
- InhaltsvorschauAutomatically testing user interfaces has always been difficult. Indeed, for many projects, it has often been placed in the “too-hard” basket. In this chapter, we will look at two tools that can help with your automatic Graphical User Interface (GUI) testing: Selenium and Fixtures for Easy Software Testing (FEST). Selenium is an innovative tool that uses a web browser to run tests against your web application. And FEST is an equally innovative product that lets you integrate Swing testing as part of your JUnit or TestNG tests.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Testing Your Web Application with Selenium
- InhaltsvorschauWith a bit of practice and good tools, it isn’t difficult to write good unit tests for a large part of most applications. Lightweight POJO-based frameworks such as Hibernate and Spring make it easier to design classes and components that can be unit-tested in isolation. Embedded Java databases such as Derby and HSQLDB, along with database testing frameworks such as DBUnit, make it a relatively simple task to test database access layers. EJB-based applications are an exception to this rule—you generally need to deploy your EJBs onto an application server before you can test them correctly, which makes unit testing unwieldy and difficult.However, testing the user interface of a web application has always been problematic. Some libraries, such as
StrutsTestCase(see ) and the Spring MVC testing framework, make good use of mock objects approach to simulate interaction with the server. These tools fit smoothly into ordinary unit tests, and are excellent at testing (in MVC terminology) the Controller code. However, although they do a fine job of this, their limits lie in the fact that they only test the application code, and not the HTML screens .Other tools use different approaches. Some, like HttpUnit, allow you to write tests to run against a running web server, and then inspect the returned HTML code. Cactus lets you test applications by running the tests on the server itself. JMeter lets you do functional web testing to some extent, again by building HTTP requests. Frank Cohen’s Test Maker is another interesting open source product in this field that lets you record web tests in Jython, edit them, and replay them as unit tests.Selenium is a little different. Selenium is an open source testing tool, originally developed by ThoughtWorks and now hosted and maintained by OpenQA, which tests web applications by using them as a user would—via a browser. So, rather than building HTTP requests that a browser might send to the server and analyzing the results, drives a real browser, making it possible to test more sophisticated user interfaces. Selenium works on most platforms (Windows, Linux, and Mac OS X) and with most browsers (Firefox, Internet Explorer, Opera, Konqueror, Safari...), which makes Selenium a good choice when it comes to verifying cross-browser compatibility.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Swing GUIs with FEST
- InhaltsvorschauContributed by: Alex RuizGraphical User Interfaces (GUIs) have become a valuable way of interacting with computer programs. Testing GUIs is vital because it can improve the safety and fitness of the entire system. Any GUI, even the simplest one, is likely to enclose some level of complexity. Complexity in software needs to be tested because untested code is a potential source of bugs.GUI testing is also important during application maintenance. During this stage, code might be refactored repeatedly to improve its design, and this code often includes great portions of the user interface. Having a solid test suite that covers the GUI code can give us assurance that we are not unintentionally introducing bugs.This section introduces FEST, an open source library that facilitates functional GUI testing, and some practices that can simplify the creation and maintenance of thorough tests for Java Swing applications.Although essential, GUI testing can be difficult. Conventional unit testing, such as testing a class in isolation, normally is not appropriate for GUI testing: A GUI “unit” can be made up of more than one component, each of them enclosing more than one class. In many cases, functional testing is a more effective way to test GUIs.The following factors are necessary to creating thorough functional GUI tests:
- Being able to simulate user events
- Having a reliable mechanism for finding GUI components
- Being able to tolerate changes in a component’s position and/or layout
FEST (Fixtures for Easy Software Testing) is an open source library, licensed under Apache 2.0, which makes it easy to create and maintain large functional GUI tests. Although several open source projects have been devised for testing GUIs, FEST is distinguished by the following features:- An easy-to-use Java API that exploits the concept of fluent interfaces to simplify coding.
- Assertion methods that detail the state of GUI components.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauIn spite of its importance, testing GUIs is difficult. FEST is an open source library that provides an easy-to-use API for GUI testing. FEST makes it easier to write and maintain robust GUI tests, which gives you more time to focus on what matters: specifying and verifying the behavior of your Swing GUIs.FEST is a useful alternative to existing GUI-testing solutions. It’s easy to learn and use, and it provides some unique features that can make GUI development more productive and fun.Future improvements will cover support for third-party GUI components, such as the ones provided by SwingLabs’ SwingX, and an easy-to-use Groovy and JRuby API for GUI tests.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 21: Detecting and Enforcing Coding Standards with Checkstyle
- InhaltsvorschauCheckstyle is an open source tool that enforces coding conventions and best practice rules for Java code. Although it was originally designed to enforce coding standards, it now lets you verify coding best practices as well, in much the same way as PMD () and FindBugs (). It works by analyzing Java source code and reporting any breach of standards. It can be integrated into your favorite IDE via a plug-in so that developers can immediately see and correct any breaches of the official standards. It can also be used to generate project-wide reports that summarize the breaches found.Checkstyle comes “out-of-the-box” with the standard Sun conventions, including more than 120 rules and standards, dealing with issues that range from code formatting and naming conventions to Enterprise JavaBean (EJB) best practices and code complexity metrics. Checkstyle supports standards related to the :
- Javadoc comments
- Naming conventions
- File headers
- Import statements
- Whitespace
- Modifiers
- Blocks
- Coding problems
- Class design
- J2EE (Java 2 Platform, Enterprise Edition)
- And other miscellaneous issues
You can run Checkstyle from the command line, if you are so inclined. Download the Checkstyle distribution from the web site and extract it in a convenient place. Then run the checkstyle-all-4.3.jar file as shown here:$ java -jar checkstyle-all-4.3.jar -c sun_checks.xml -r srcThis will analyze the code in the specified source directory and list any rule violations it finds. Here is an example, running the Sun Coding Standards provided with Checkstyle against one of the Java EE 5 sample applications provided by Sun:$ java -jar checkstyle-all-4.3.jar -c sun_checks.xml -r javaee5/webservices/ hello-jaxws/src/ Starting audit... /home/john/tools/checkstyle-4.3/javaee5/webservices/hello-jaxws/src/ endpoint/package.html:0: Missing package documentation file. /home/john/tools/checkstyle-4.3/javaee5/webservices/hello-jaxws/src/ client/package.html:0: Missing package documentation file. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:7:1: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:5: Method 'getHello' is not designed for extension - needs to be abstract, final or empty. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:28: Parameter name should be final. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:9:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:13: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:14:1: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:15:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:15:32: '=' is not preceded with whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:15:33: '=' is not followed by whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:16:25: Variable 'service' must be private and have accessor methods. javaee5/webservices/hello-jaxws/src/client/Client.java:17: L ine has trailing spaces. javaee5/webservices/hello-jaxws/src/client/Client.java:18:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:18:29: Parameter args should be final. javaee5/webservices/hello-jaxws/src/client/Client.java:19:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:23: Line has trailing spaces. javaee5/webservices/hello-jaxws/src/client/Client.java:24:5: Method 'doHello' is not designed for extension - needs to be abstract, final or empty. javaee5/webservices/hello-jaxws/src/client/Client.java:24:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:25:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:27:9: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:31:9: '}' should be on the same line. javaee5/webservices/hello-jaxws/src/client/Client.java:32:14: 'catch' is not followed by whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:33:9: '{' should be on the previous line. Audit done.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Checkstyle to Enforce Coding Standards
- InhaltsvorschauCheckstyle is an open source tool that enforces coding conventions and best practice rules for Java code. Although it was originally designed to enforce coding standards, it now lets you verify coding best practices as well, in much the same way as PMD () and FindBugs (). It works by analyzing Java source code and reporting any breach of standards. It can be integrated into your favorite IDE via a plug-in so that developers can immediately see and correct any breaches of the official standards. It can also be used to generate project-wide reports that summarize the breaches found.Checkstyle comes “out-of-the-box” with the standard Sun conventions, including more than 120 rules and standards, dealing with issues that range from code formatting and naming conventions to Enterprise JavaBean (EJB) best practices and code complexity metrics. Checkstyle supports standards related to the :
- Javadoc comments
- Naming conventions
- File headers
- Import statements
- Whitespace
- Modifiers
- Blocks
- Coding problems
- Class design
- J2EE (Java 2 Platform, Enterprise Edition)
- And other miscellaneous issues
You can run Checkstyle from the command line, if you are so inclined. Download the Checkstyle distribution from the web site and extract it in a convenient place. Then run the checkstyle-all-4.3.jar file as shown here:$ java -jar checkstyle-all-4.3.jar -c sun_checks.xml -r srcThis will analyze the code in the specified source directory and list any rule violations it finds. Here is an example, running the Sun Coding Standards provided with Checkstyle against one of the Java EE 5 sample applications provided by Sun:$ java -jar checkstyle-all-4.3.jar -c sun_checks.xml -r javaee5/webservices/ hello-jaxws/src/ Starting audit... /home/john/tools/checkstyle-4.3/javaee5/webservices/hello-jaxws/src/ endpoint/package.html:0: Missing package documentation file. /home/john/tools/checkstyle-4.3/javaee5/webservices/hello-jaxws/src/ client/package.html:0: Missing package documentation file. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:7:1: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:5: Method 'getHello' is not designed for extension - needs to be abstract, final or empty. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:8:28: Parameter name should be final. javaee5/webservices/hello-jaxws/src/endpoint/Hello.java:9:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:13: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:14:1: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:15:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:15:32: '=' is not preceded with whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:15:33: '=' is not followed by whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:16:25: Variable 'service' must be private and have accessor methods. javaee5/webservices/hello-jaxws/src/client/Client.java:17: L ine has trailing spaces. javaee5/webservices/hello-jaxws/src/client/Client.java:18:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:18:29: Parameter args should be final. javaee5/webservices/hello-jaxws/src/client/Client.java:19:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:23: Line has trailing spaces. javaee5/webservices/hello-jaxws/src/client/Client.java:24:5: Method 'doHello' is not designed for extension - needs to be abstract, final or empty. javaee5/webservices/hello-jaxws/src/client/Client.java:24:5: Missing a Javadoc comment. javaee5/webservices/hello-jaxws/src/client/Client.java:25:5: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:27:9: '{' should be on the previous line. javaee5/webservices/hello-jaxws/src/client/Client.java:31:9: '}' should be on the same line. javaee5/webservices/hello-jaxws/src/client/Client.java:32:14: 'catch' is not followed by whitespace. javaee5/webservices/hello-jaxws/src/client/Client.java:33:9: '{' should be on the previous line. Audit done.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Checkstyle in Eclipse
- InhaltsvorschauCheckstyle can be used in a variety of ways, including from the command line or from within an Ant or Maven build script. However, from a developer’s perspective, the most efficient way to use it is from within the IDE, as corrections can be made immediately and with little effort. Plug-ins exist for most of the major Java IDEs, including Eclipse, IntelliJ, NetBeans, JBuilder, and even JEdit. In this section, we go through how this is done using Eclipse 3.2 and Checkstyle 4.2.The Eclipse Checkstyle plug-in is found at . The first thing you will need to do is to install the plug-in. Just use the plug-in’s Remote Update site, which can be found at (see ).
Figure : Installing Checkstyle on EclipseTo install this plug-in, you follow the usual procedure for installing an Eclipse plug-in:- Open the Plug-in installation window by selecting the “Help→Software Updates→Find and Install” menu.
- Select “Search for New Features.”
- Create a new remote site entry for the Checkstyle update server via the “New Remote Site” button.
- Enter a name for the remote site (say “Checkstyle Plug-in”) and the site URL (see ).
- Step through the following installation screens.
Once the plug-in is installed, you need to activate Checkstyle checks for your particular project. By default, Checkstyle will not be activated for your projects: you need to set this up yourself. Open the project properties window (Project→Properties), and select the Checkstyle properties entry (see ). Make sure that the “Checkstyle active for this project” checkbox is selected.You can also specify the set of rules you want to use for this project. The Eclipse Checkstyle plug-in comes out-of-the-box with two sets of rules: the Sun Java Coding Standards and a slightly modified version of these rules, better adapted to the standard Eclipse formatting conventions. You can use one of these to get started, although you will probably want to tailor the rules to your own needs later on (see ).A Checkstyle code audit can be time-consuming, especially if there are a lot of files to check. Too many errors can also make it harder to focus on the ones that really should be fixed. The “Exclude from checking...” option lets you filter out certain classes or packages that you don’t need (or want) to be checked, which can save time and processing power.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Checkstyle Rules in Eclipse
- InhaltsvorschauThe Sun coding standards—provided out-of-the-box via a configuration file—can be overwhelming at times. Checkstyle may well come up with hundreds of relatively minor rule violations (whitespace at the ends of lines, for example), especially if a lot of code has already been written without Checkstyle in place.The more important standards may get lost in the mass of minor and relatively unimportant ones. This can result in two possible negative side effects:
- Developers will become discouraged and ignore all rule violations, which tends to defeat the purpose of installing Checkstyle in the first place.
- Developers will become overly zealous and pass hours removing spaces from the ends of lines. This will produce nice clean code, but will probably slow down developer productivity considerably.
To optimize the adoption of coding standards and the use of Checkstyle to enforce them, you often need to take a more flexible approach. The easiest way to do this is to create a custom set of coding standards that is specifically designed either for your company or for a single project.You can do this fairly easily from within Eclipse (see Figure 21-4). Go into the Preferences window, and select the Checkstyle preferences. You will see some built-in configuration files (the Sun coding standards and a slightly modified version of the Sun standards adapted to the default code formatting used by Eclipse).To create a new Checkstyle configuration file, just click “New.” Several types of configuration files are available, including:- Built-in configurations
- For example, the Sun coding standards, which are delivered with the Checkstyle plug-in, and which cannot be altered.
- Internal configurations
- Stored within the Eclipse metadata. These are useful for experimenting locally with new configurations, but they cannot (easily) be shared with other team members.
- External configurations
- Imported from an external source. External configurations may be imported from an external file on a local disk (“External Configuration”) or from a web server (“Remote Configuration”), or be stored within the Eclipse project, under configuration management, for example, (“Project Relative Configuration”).
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Checkstyle Rules Using the XML Configuration Files
- InhaltsvorschauUnder the hood, a Checkstyle configuration file is a standard XML file that defines a set of modules that are used to verify source code. Each module is specialized in one particular kind of coding standard or best practice.The configuration file is basically an XML representation of a hierarchy of modules, starting from a special root module called the Checker module, which contains other modules. Some modules can also contain submodules. This is notably the case of the TreeWalker module, which parses a Java source file and then lets submodules verify different aspects of the parse tree. In fact, the majority of Checkstyle modules are sub-modules of the TreeWalker module.A very simple Checkstyle configuration file might look like this:
<?xml version="1.0"?> <!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> <module name="Checker"> <module name="TreeWalker"> <property name="tabWidth" value="4"/> <property name="charset" value="UTF-8"/> <module name="JavadocMethod"/> <module name="JavadocVariable"/> <property name="scope" value="protected"/> </module> <module name="AvoidStarImport"/> </module> <module name="PackageHtml"/> </module>As with all Checkstyle configuration files, this file starts with the special Checker root module. Next we add the TreeWalker module. This module parses individual Java source code files. We have added two properties to this module: tabWidth, which defines the number of spaces represented by a tab character when calculating line lengths, and charset, which (in this case) allows Checkstyle to handle UTF-8 source code files.While we are in the process of parsing a Java source code file, we can also apply a number of other checks. These are represented by nested modules within the TreeWalker module. In fact, the majority of modules must be nested in the TreeWalker module, for the simple reason that most checks invovlve a particular Java class. In this case, we have added the following methods:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Checkstyle: Common Rules That You Can Do Without, and Some That You Could Use
- InhaltsvorschauThe rules that Checkstyle provides out-of-the-box implement the Sun Coding Standards, but you may want to be a little more flexible, especially concerning arguably minor issues such as whitespaces. In and , we discussed how you can create your own set of Checkstyle rules, adapted to your development practices. Here are a few pragmatic guidelines about which rules you may want to keep and which rules you may want to discard, based on the out-of-the-box Sun Checks:
- Javadoc comments
- These rules are generally good to have, as they help to encourage better quality technical documentation. You can probably do without the Package Html rule, which checks for the presence of a package.html file in each package, a recommendation that is rarely followed with any consistency in the real world.
- Naming conventions
- Naming conventions play an important role in code readability—I generally stick to the Sun conventions here.
- Headers
- The rules about headers are not included in the default Checkstyle configurations, as they tend to be company-specific. Don’t bother with these unless you have some company-wide obligation to do so.
- Imports
- The conventions around imports (no star (*) or redundant imports, imports in alphabetical order...) are beneficial for code readability, and are also supported by modern IDEs such as Eclipse and NetBeans with little extra effort. For example, in Eclipse, the “Source→Organize Imports” menu will automatically fix any issues. I usually keep the default configuration here.
- Size violations
- These limits on class and method size are designed to keep code short and simple, as recommended by many Java best practices and Agile methodologies. They are good to have.
- Whitespace
- The Checkstyle whitespace rules contain an abundance of rules about where white space is (or isn’t) required. Although many correspond to standard Java coding practices and make for more readable code, some are a little too pedantic for real-world use.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Defining Rules for Source Code Headers with Checkstyle
- InhaltsvorschauMany companies and projects use a standard file header convention. Checkstyle offers many ways to enforce this. For simple cases, you can write a header template, where some lines are fixed and others may be modified by the developer. Suppose that your company standards impose a boxed-style comment with a copyright line at the bottom, as shown here:
//////////////////////////////////////////////////////////////////// // My Project Title // A description of this file goes here. // // Copyright (C) 2006 My Company ////////////////////////////////////////////////////////////////////
One easy way to do this is to define a header template called java.header, which would contain the above text, and then indicate which lines may be modified:<module name="Header"> <property name="headerFile" value="java.header"/> <property name="ignoreLines" value="2, 3, 4"/> </module>Suppose that all you need to do is to put a copyright line at the top of each file:// Copyright (C) 2006 MyCompany // All rights reserved
At first sight, this looks like a static block of text. However, the year needs to change each year. To do this, you can define an inline regular expression using the Header module, as shown here:<module name="RegexpHeader"> <property name="header" value="^// Copyright \(C\) \d\d\d\d My Company$\n^// All rights reserved$"/> </module>You can also define the header as a more complicated regular expression in an external file. Suppose your company or project standards require a file header containing dynamic elements coming from the source configuration system, as in the following example://////////////////////////////////////////////////////////////////// // My Project Title // File: $Id$ // A short description goes here // // Last modified $Date$ by $Author $ // Copyright (C) 2006 My Company ////////////////////////////////////////////////////////////////////
This can be configured using the RegexpHeader module and an external file template (called “java.header” in this example):Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Suppressing Checkstyle Tests
- InhaltsvorschauThere will be times when you come across a genuine reason for violating a coding standard for a particular section of code. For example, the following code extracts the list of students in each percentile:
for (int i = 1; i < 100; i++) { List<Student> students = extractCentile(i, examResults); … }In this context, the use of the value 100, for example, is clear, and there is little added value in replacing it by a constant called ONE_HUNDRED. Checkstyle lets you get around this problem in several ways. The easiest way to deal with particular cases like this is to use the SuppressionCommentFilter module. This module lets you deactivate Checkstyle for a section of code:// CHECKSTYLE:OFF – 100 is not a "magic number" in this case for (int i = 1; i < 100; i++) { // CHECKSTYLE:ON List<Student> students = extractCentile(i, examResults); … }Another way to do this is to use the SuppressionFilter associated with an XML configuration file, where detailed suppressions can be specified. This approach is useful for deactivating rules for large blocks of code or across several classes:<module name="SuppressionFilter"> <property name="file" value="docs/suppressions.xml"/> </module>The code above calls a suppressions.xml file, a file that you need to write where you can deactivate particular checks for particular classes, or even for certain lines in a particular class. In the following example, all Javadoc checks are deactivated for the first 50 lines of the Catalog class, and the MagicNumberCheck is deactivated for all unit test classes:<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.0//EN" "http://www.puppycrawl.com/dtds/suppressions_1_0.dtd"> <suppressions> <suppress checks="Javadoc*" files="Catalog.java" lines="1-50"/> <suppress checks="MagicNumberCheck" files="*Test.java"/> </suppressions>This approach does require extra maintenance work to keep the suppression.xml file up-to-date. Developers also may be tempted to use wildcards a little excessively, which can reduce the efficiency of Checkstyle audits. In practice, this method should be used sparingly and only after other options have been considered and eliminated.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Checkstyle with Ant
- InhaltsvorschauIntegrating Checkstyle into your standard build process is an important step in the road to improved code quality. We will look at how to integrate Checkstyle into a build process using Ant.Checkstyle comes out-of-the-box with an Ant task that does just this. If you haven’t already done so, download the Checkstyle installation file and unzip it into some convenient directory. Here, we have installed it into the “${user.home}/tools” directory:
$ cd ~/tools $ unzip checkstyle-4.3.zip
The next thing you need to do is to add a task definition for the Checkstyle task. Something along the following lines should do:<property name="checkstyle.home" location="${user.home}/tools/checkstyle-4.3" /> <taskdef resource="checkstyletask.properties" classpath="${checkstyle.home}/checkstyle-all-4.3.jar"/>Then, to run Checkstyle against your source code, just invoke the task as follows:<checkstyle config="${checkstyle.home}/sun_checks.xml"> <fileset dir="src" includes="**/*.java"/> <formatter type="plain"/> <formatter type="xml"/> </checkstyle>The only problem with this is that the results are not particularly readable. If the error messages are for human consumption, you should pass the generated XML through a stylesheet (several are provided with the Checkstyle distribution) to generate the results in a more readable HTML form:<target name="checkstyle"> <checkstyle config="${checkstyle.home}/sun_checks.xml" failOnViolation="false"> <fileset dir="src" includes="**/*.java"/> <formatter type="plain"/> <formatter type="xml" tofile="checkstyle-report.xml"/> </checkstyle> <xslt in="checkstyle-report.xml" style="${checkstyle.home}/contrib/checkstyle-noframes.xsl" out="reports/checkstyle-report.html"/> </target>Don’t forget the failOnViolation attribute. If this is not set to “false,” the Checkstyle check will stop the build if it finds any violations, and never get to the report generation task.Now you can run the task:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Checkstyle with Maven
- InhaltsvorschauCheckstyle integrates extremely well with Maven, which comes with a Checkstyle report plug-in (see ). The Maven Checkstyle report contains summaries and more detailed information about the different detected issues in the code, with data presented using a convenient drill-down approach.You set up the basic configuration in the <reporting> section of your pom.xml file, as shown here:
<reporting> <plugins> <plugin> <artifactId>maven-checkstyle-plugin</artifactId> <configuration> <configLocation>src/main/config/company_checks.xml</configLocation> </configuration> </plugin> </plugins> </reporting>The optional <configuration> tag lets you specify your own customized set of rules (in and ). By default, the standard Sun Java Coding Conventions will be used.If you use a suppressions file (in ), you can also specify this file in the suppressionsLocation configuration entry, as shown here:<reporting> <plugins> <plugin> <artifactId>maven-checkstyle-plugin</artifactId> <configuration> <configLocation>src/main/config/company_checks.xml</configLocation> <suppressionsLocation>suppressions.xml</suppressionsLocation> </configuration> </plugin> </plugins> </reporting>In the same way, you can use headerLocation to specify a header template ().You run the Checkstyle report by generating the maven site using the “mvn site” command (see ), or directly from the command line, as shown here:$ mvn checkstyle:checkstyleA typical report is illustrated in .The Maven Checkstyle report is flexible and takes a large number of configuration options that you can use to fine-tune the layout and content of the report. For example, by default the Checkstyle report begins with a list of all the rules used. You can remove this by setting the enableRulesSummary configuration entry to false. If you want the build to stop if there are any Checkstyle errors, set the failsOnErrorEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 22: Preemptive Error Detection with PMD
- InhaltsvorschauPMD is a static code analysis tool, capable of automatically detecting a wide range of potential defects and unsafe or nonoptimized code. Whereas other tools, such as Checkstyle (see ), can verify that coding conventions and standards are respected, PMD focuses more on preemptive defect detection. It comes with a rich and highly configurable set of rules, and you can easily configure which particular rules should be used for a given project. PMD integrates well with IDEs such as Eclipse and NetBeans, and it also fits well into the build process thanks to its smooth integration with Ant and Maven.PMD is designed to integrate well into a developer’s work environment. Plug-ins exist for the principal IDEs, and they are the most productive and convenient way for a developer to use PMD. Plug-ins allow almost real-time code verification—they raise issues whenever you save the source code.The easiest way to install and use PMD under Eclipse is to use the remote update site, at . You do this in the usual way:
- Open the Help→Software Updates→Find and Install menu.
- Click Next, and choose New Remote Site.
- Now enter the URL of the remote site (), and an appropriate name such as “PMD.”
- Make sure that you have the PMD site checked in the “Sites to include in search” window, and click Finish. Then just go through the installation screens to install the plug-in.
Once you have installed the plug-in, you need to activate PMD for your project. Open the project properties window (Project→Properties). You will now have a PMD entry (see ). This window allows you to configure PMD in detail for your particular project, by selecting which rules you want to apply, and specifying their relative importance. For now, just check the “Enable PMD” box and leave the default set of rules.
Figure : Configuring PMD for a projectTo run PMD, click on the project resource and select “PMD→Check code with PMD” in the contextual menu.PMD rule violations are displayed as entries in the “Tasks” view (see ), with a priority (high, medium, or low) depending on the priority of the corresponding rule. As with any other task, clicking on the task will take you straight to the offending code. Some developers find this view convenient as a personal productivity tool: PMD issues are listed among other tasks (such as TODOs), and you can sort tasks by priority, manually reassign priorities, mark tasks as “done,” and so on.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - PMD and Static Code Analysis
- InhaltsvorschauPMD is a static code analysis tool, capable of automatically detecting a wide range of potential defects and unsafe or nonoptimized code. Whereas other tools, such as Checkstyle (see ), can verify that coding conventions and standards are respected, PMD focuses more on preemptive defect detection. It comes with a rich and highly configurable set of rules, and you can easily configure which particular rules should be used for a given project. PMD integrates well with IDEs such as Eclipse and NetBeans, and it also fits well into the build process thanks to its smooth integration with Ant and Maven.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Using PMD in Eclipse
- InhaltsvorschauPMD is designed to integrate well into a developer’s work environment. Plug-ins exist for the principal IDEs, and they are the most productive and convenient way for a developer to use PMD. Plug-ins allow almost real-time code verification—they raise issues whenever you save the source code.The easiest way to install and use PMD under Eclipse is to use the remote update site, at . You do this in the usual way:
- Open the Help→Software Updates→Find and Install menu.
- Click Next, and choose New Remote Site.
- Now enter the URL of the remote site (), and an appropriate name such as “PMD.”
- Make sure that you have the PMD site checked in the “Sites to include in search” window, and click Finish. Then just go through the installation screens to install the plug-in.
Once you have installed the plug-in, you need to activate PMD for your project. Open the project properties window (Project→Properties). You will now have a PMD entry (see ). This window allows you to configure PMD in detail for your particular project, by selecting which rules you want to apply, and specifying their relative importance. For now, just check the “Enable PMD” box and leave the default set of rules.
Figure : Configuring PMD for a projectTo run PMD, click on the project resource and select “PMD→Check code with PMD” in the contextual menu.PMD rule violations are displayed as entries in the “Tasks” view (see ), with a priority (high, medium, or low) depending on the priority of the corresponding rule. As with any other task, clicking on the task will take you straight to the offending code. Some developers find this view convenient as a personal productivity tool: PMD issues are listed among other tasks (such as TODOs), and you can sort tasks by priority, manually reassign priorities, mark tasks as “done,” and so on.PMD also lets you visualize issues in other views that are more specifically tailored to PMD. The “Violation Outlines” view lists PMD issues by severity. To open this view, open the “Window→Show View →Other” menu, and choose the “PMD→Violations Outline” view (see ).
Figure : PMD displays rule violations as tasksEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring PMD Rules in Eclipse
- InhaltsvorschauWhen you introduce coding standards and best practices into an organization, it is important to tailor the rules to your exact needs. This should be a team effort—get everyone who’s going to be applying the rules involved. Each PMD rule has a detailed description and examples, available both on the web site and visible in the configuration screens. Review each rule and come to a joint decision on whether and when that rule should be applied in your organization. The most convenient place to configure the PMD ruleset is from within Eclipse, in the PMD entry of the “Windows→Preferences→PMD→Rules configuration” window (see ). This window contains a list of all the available PMD rules. From this list, you can go through the rules, adjust rule priorities, modify any of the other rule-specific properties, and also remove any rules you don’t need. You can also build a ruleset from scratch: just delete all the current rules (“Clear All”) and then import selected individual rulesets one by one (See ).When you’re happy with your new customized ruleset, you can export it in the form of an XML file (“Export Rule Set”). Other team members can now clear their existing ruleset and import the new ruleset into their environments. You can also activate or deactivate individual rules for a project in the project properties window (see Figure 11). And if you do anything really silly, you can always get back to the default ruleset using the “Restore Defaults” button.
Figure : Configuring PMD rules from scratchEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - More on the PMD Rulesets
- InhaltsvorschauPMD is a powerful and highly configurable tool. It delivers a rich set of more than 190 rules, and you can easily write additional ones if need be. PMD rules are divided into rulesets, each of which contains rules concerning a particular type of issue. We will run through the rulesets, and give some indication as to when each ruleset is or is not appropriate.
- Basic rules (the “basic” ruleset)
- These rules verify some common, and useful, best practices, such as empty catch blocks or if statements, unnecessary temporary conversions when converting primitives to Strings, and so on.
- JSF rules (the “basic-jsf” ruleset)
- This ruleset is designed to help improve the quality of JSF pages. At the time of writing, it relatively limited, containing only one rule. However, considering the current momentum of JSF, new rules will probably be added in future versions. It may be worthwhile including these rules if your project uses JSF with JSP/JSTL pages.
- JSP rules (the “basic-jsp” ruleset)
- This ruleset contain some useful rules designed to help enforce quality coding in JSP pages. Examples of these rules include using tag libraries rather than scriptlets, and placing all style information in CSS files rather than with the HTML tags in the JSP page. Include this ruleset if your project uses JSP.
- Braces rules (the “braces” ruleset)
- These are coding standards that check that if, else, while, and for statements use curly braces. If you also are using Checkstyle to enforce coding conventions, these rules should be unnecessary.
- Clone Implementation rules (the “clone” ruleset)
- These stipulate best practices that should be applied when implementing the clone() method: always invoke super.clone(); the class should implement Cloneable; and the clone() method should throw CloneNotSupportedException. This is handy if your project needs to use the
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Writing Your Own PMD Ruleset
- InhaltsvorschauFor convenience, PMD rules are divided into rulesets (see ). However, once you become familiar with the various rules, you may want to define a custom set of rules coming from different rulesets, in order to centralize maintenance and simplify project configuration. You can do this by writing your own PMD ruleset.A PMD ruleset is simply an XML file that lists a set of rules that you wish to use. You can include entire rulesets, or selectively choose specific rules from within other rulesets. You can also provide extra parameters to certain rules in order to customize their behavior.Suppose that we want to write our own company ruleset. To start off, our ruleset will contain the basic, unusedcode, string, and junit rulesets. We create a new <ruleset> XML file, containing references to these rulesets:
<?xml version="1.0" ?> <ruleset name="CompanyRules" xmlns="http://pmd.sf.net/ruleset/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> <description>CompanyRules</description> <rule ref="rulesets/basic.xml" /> <rule ref="rulesets/junit.xml" /> <rule ref="rulesets/strings.xml" /> <rule ref="rulesets/unusedcode.xml" /> </ruleset>In addition, we may want to include some, but not all, of the rules in the design ruleset. To do this, we add explicit references to the rules, rather than to the ruleset as a whole:<rule ref="rulesets/design.xml/UseSingleton" /> <rule ref="rulesets/design.xml/SimplifyBooleanReturns" /> <rule ref="rulesets/design.xml/EqualsNull" />
Some rules may need to be configured, to override the default property values. For example, we may want to be particularly strict on code complexity. So we will configure the McCabe Cyclometric Complexity rule (see ) to allow at most moderately complex methods, with a cyclometric complexity of no more than 7:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Generating a PMD Report in Eclipse
- InhaltsvorschauMany people are very attached to hard-copy outputs. If you are among them, you may appreciate the ability to generate PMD rule violation reports in CSV, HTML, TXT, and XML formats. Just go to the contextual menu on the project, and select “PMD→Generate Reports.” The reports will be generated in the /report directory of the current project. shows an example of an HTML report.
Figure : Generating a report in PMDEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Suppressing PMD Rules
- InhaltsvorschauAll rules have exceptions. You will have occasions when PMD gets it wrong, and you have a legitimate reason for not respecting one of the PMD rules. For example, consider the following code:
/** Countries : USA */ public static final Country USA = new Country("us","United States");Suppose that your company standards impose a minimum of four letters for variable names. In this case, PMD will incorrectly generate an error. To get around this, you can mark a violation as “Reviewed,” which basically tells PMD that you’ve seen the issue and that it’s fine by you. Click on the error and open the contextual menu, then select “Mark as reviewed.” PMD will insert a special comment similar to the following:/** Countries : USA */ // @PMD:REVIEWED:ShortVariable: by taronga on 4/13/06 7:25 AM public static final Country USA = new Country("us","United States");As long as you don’t remove it, PMD will now ignore this violation for this particular case.Another way of doing this while writing the code is to use the “NOPMD” marker, as follows:// These are x and y coordinates, so short variable names are OK int x = 0; // NOPMD int y = 0; // NOPMDThe marker deactivates the ShortVariable rule for the variables.If you are using JDK 1.5, you can also use the PMD SuppressWarnings annotation.This technique is particularly useful for generated classes or legacy code. In the following class, all PMD warnings are suppressed:@SuppressWarnings("") public class Country { ... }You may just want to suppress certain rules for a given class. In the following generated class, for example, private variables are prefixed with an underscore, which is not in line with PMD’s rules concerning JavaBeans. To get around this, just suppress a specific PMD rule:@SuppressWarnings("BeanMembersShouldSerialize") public class Country { private String _code; ... public String getCode(){ return _code; } }Sometimes, the rule may not be what you expect. In this case, for example, PMD expects a getter and setter for each nontransient and nonstatic class member variable. If a variable doesn’t have a proper getter and setter, PMD will complain by saying that the variable is not serializable, and should be either transient or static, or have standardized getters and setters. So the naming conventions for accessor members are covered by this rule.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Detecting Cut-and-Paste with CPD
- InhaltsvorschauCutting and pasting code between classes is a bad habit. Areas of cut-and-pasted code increase maintenance costs unnecessarily, and indicate in the very least a good candidate for refactoring. In many cases, they are high-risk zones for potential errors.PMD comes with a useful tool for detecting cut-and-pasted code called CPD (Cut-and-Paste Detector). You can run it from the contextual menu on the project, using the “PMD→Find Suspect Cut and Paste” menu option. Unfortunately, at the time of this writing, the results of this tool were not integrated into the IDE. The tool generates a text file called cpd-report.txt in the /report directory, which contains copy-and-paste suspects, as shown here:
===================================================================== Found a 18 line (56 tokens) duplication in the following files: Starting at line 69 of /home/taronga/Documents/articles/HotelWorld/src/main/java/com /wakaleo/tutorials/hotelworld /model/HotelModel.java Starting at line 82 of /home/taronga/Documents/articles/HotelWorld/src/main/java/com /wakaleo/tutorials/hotelworld /model/HotelModel.java List hotelsFound = findHotelsByLanguage(language); Hotel hotel = null; for(int i = 0; i < hotels.length; i++) { hotel = (Hotel) hotels[i]; if (hotel.getCity().equalsIgnoreCase(city)) { hotelsFound.add(hotel); } } return hotelsFound; } /** * Find hotels where a given language is spoken. * @param language * @return */ public List findHotelsByLanguage(Language language) {You can customize the minimum size of a copy-and-paste zone suspect in the workbench preferences under PMD→CPD Preferences. Just adjust the “Minimum tile size” field, and specify the minimum number of lines.CPD is not limited to use within your IDE: you can also run CPD as a command-line tool, as an Ant task, or from within Maven. To run CPD as an Ant task, you first need to define a “cpd” task (using theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using PMD in Ant
- InhaltsvorschauStatic analysis tools such as PMD can be used in two complementary ways, both of which have their place in the software development lifecycle. Developers use PMD most effectively from within the IDE, where they can quickly and interactively detect and fix issues. Lead developers, project managers, and quality assurance people, by contrast, prefer to see PMD integrated into the Continuous Build process, producing static reports, so that they can monitor code quality and potential issues over the whole project.PMD comes with a flexible Ant task you can use to generate PMD reports. To get this to work, you need to copy the PMD jar to your Ant lib directory. Download the latest version of PMD from the web site and decompress the package in a convenient place. On my Linux machine, for example, I placed it in the /usr/local/tools directory and created a symbolic link (to make updating the library easier):
$ cd ~/tools $ unzip pmd-bin-4.1.zip $ ln -s pmd-4.1 pmd
Now you need to copy the PMD library to your Ant lib directory. You also need the other libraries required by PMD: there are two or three stored alongside the PMD library in the distributed bundle. On my installation, the Ant lib directory is in/usr/share/ant/lib, but this will obviously change depending on your exact environment. The easiest approach is to simply copy the whole lot into the Ant directory:$ cd ~/pmd/ $ cp lib/*.jar /usr/share/ant/lib/
You can then declare the PMD task as follows:<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask"/>
A possibly more robust approach involves providing a classpath for the PMD task definition. You need to set up a classpath that points to the PMD jar files. Using this approach, you don’t need to modify the Ant configuration on each machine, and the PMD JAR files can be stored in some central location. In the following code, we assume that PMD is installed as described above:<property name="pmd.home" location="${user.home}/tools/pmd-4.1" /> <path id="pmd.classpath"> <fileset dir="${pmd.home}/lib" includes="*.jar" /> </path> <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="pmd.classpath"/> <taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" classpathref="pmd.classpath"/>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using PMD in Maven
- InhaltsvorschauLike Checkstyle (see ), PMD is well integrated with Maven, which is packaged with a PMD report plug-in. This plug-in will generate both the PMD and CPD reports, with cross-references to the HTML version of the source code generated by JXR (if the JXR report has been included—see ).To set up basic PMD reporting in your Maven 2 project, just add a reference to the PMD plug-in in the reporting section of your pom.xml file:
<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> </plugin> </plugins> </reporting>You can generate the report by either generating the entire Maven site (using the mvn site command), or by invoking the PMD report directly:$ mvn pmd:pmdThis will generate the PMD and CPD reports in thetarget/sitedirectory.You will generally need to fine-tune the plug-in configuration by adding some extra parameters to the <configuration> element. For example, if your project uses JDK 1.5, you will need to specify this with the <targetJdk> configuration element. Another useful configuration element is <failonerror>, which forces build failure whenever errors are detected:<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <configuration> <targetJdk>1.5</targetJdk> <failonerror>true</failonerror> </configuration> </plugin> </plugins> </reporting>You can also specify the exact rulesets you want to use (see ). By default, the basic, imports, and unusedcode rulesets will be used. If you want to specify another set of rulesets, you list them in the <ruleset> element, as follows:<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <configuration> <targetJdk>1.5</targetJdk> <rulesets> <ruleset>/rulesets/basic.xml</ruleset> <ruleset>/rulesets/javabeans.xml</ruleset> <ruleset>/rulesets/junit.xml</ruleset> <ruleset>/rulesets/controversial.xml</ruleset> </rulesets> </configuration> </plugin> </plugins> </reporting>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 23: Preemptive Error Detection with FindBugs
- InhaltsvorschauFindBugs is another static analysis tool for Java, similar in some ways to Checkstyle (see ) and PMD (see ), but with a quite different focus. FindBugs is not concerned by formatting or coding standards and only marginally interested in best practices: in fact, it concentrates on detecting potential bugs and performance issues. It does a very good job of finding these, and can detect many types of common, hard-to-find bugs. Indeed, FindBugs is capable of detecting quite a different set of issues than PMD or Checkstyle with a relatively high degree of precision. As such, it can be a useful addition to your static analysis toolbox.FindBugs was written in response to the overwhelming number of issues raised by other tools such as Checkstyle and PMD. Many of the issues raised by these tools are actually false positives and both tools need to be fine-tuned to avoid real issues being hidden by too many false positives. FindBugs tries hard to concentrate on identifying only issues that involve genuine potential coding errors.FindBugs is the result of a research project at the University of Maryland. It uses static code analysis to detect potential bugs using the notion of “bug patterns.” Bug patterns are poor coding practices that are generally incorrect and may lead to application errors. For example, in the following code, if the address variable is null, the second line will generate a
NullPointerException:Address address = client.getAddress(); if ((address != null) || (address.getPostCode() != null)) { ... }Another example is shown here, in which theitemsmember variable is accessed without having been initialized:public class ShoppingCart { private List items; public addItem(Item item) { items.add(item); } }Errors like these are often easy to identify simply by reading the code. However, although effective, code reviews are labor-intensive and time-consuming, and wherever possible it is easier to let the machine do the inspection for you! FindBugs is designed to do just that.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - FindBugs: A Specialized Bug Killer
- InhaltsvorschauFindBugs is another static analysis tool for Java, similar in some ways to Checkstyle (see ) and PMD (see ), but with a quite different focus. FindBugs is not concerned by formatting or coding standards and only marginally interested in best practices: in fact, it concentrates on detecting potential bugs and performance issues. It does a very good job of finding these, and can detect many types of common, hard-to-find bugs. Indeed, FindBugs is capable of detecting quite a different set of issues than PMD or Checkstyle with a relatively high degree of precision. As such, it can be a useful addition to your static analysis toolbox.FindBugs was written in response to the overwhelming number of issues raised by other tools such as Checkstyle and PMD. Many of the issues raised by these tools are actually false positives and both tools need to be fine-tuned to avoid real issues being hidden by too many false positives. FindBugs tries hard to concentrate on identifying only issues that involve genuine potential coding errors.FindBugs is the result of a research project at the University of Maryland. It uses static code analysis to detect potential bugs using the notion of “bug patterns.” Bug patterns are poor coding practices that are generally incorrect and may lead to application errors. For example, in the following code, if the address variable is null, the second line will generate a
NullPointerException:Address address = client.getAddress(); if ((address != null) || (address.getPostCode() != null)) { ... }Another example is shown here, in which theitemsmember variable is accessed without having been initialized:public class ShoppingCart { private List items; public addItem(Item item) { items.add(item); } }Errors like these are often easy to identify simply by reading the code. However, although effective, code reviews are labor-intensive and time-consuming, and wherever possible it is easier to let the machine do the inspection for you! FindBugs is designed to do just that.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using FindBugs in Eclipse
- InhaltsvorschauFindBugs comes with an Eclipse plug-in that provides excellent integration with this IDE. Using FindBugs from within Eclipse has obvious advantages for a developer: potentially dangerous bugs can be identified and fixed even before code is committed to the repository, which allows for a much tighter development cycle.The easiest way to install the FindBugs Eclipse plug-in is to use the Eclipse Update site. You do this in the usual way:
- Open the Help→Software Updates→Find and Install menu.
- Click Next, and choose New Remote Site.
- Enter the URL of the remote site () and an appropriate name such as “FindBugs.”
- Make sure you have the FindBugs site checked in the “Sites to include in search” window, and click Finish. Then just go through the installation screens to install the plug-in.
Alternatively, you can download it from the plug-in download site and unzip the file into your Eclipse plug-in directory.Once you have installed the plug-in, you need to activate FindBugs for your project. Open the project properties window (Project→Properties). You will now have a FindBugs entry (see ). This window allows you to configure FindBugs in detail for your particular project by selecting which rules you want to apply. If you check the “Run FindBugs automatically” checkbox, FindBugs will check for issues every time you modify a class.
Figure : Configuring FindBugs for a projectYou can also filter the types of issues you want reported, either by priority or by category. For example, if you want to ignore all low priority issues, just set the minimum priority to “Medium” (this is the recommended level).Note that some FindBugs issues are quite slow, such as FindTwoLockWait, FindNullDeref, FindOpenStream, FindInconsistentSync2, and FindSleepWithLockHeld. On larger projects, you may want to disable these rules in the development environments and leave this sort of detection to the continuous integration environment.The FindBugs Eclipse plug-in is a simple, lightweight tool with few bells and whistles. If you have configured FindBugs to run automatically, it will check for bugs every time you modify a class. You can also run FindBugs using the “FindBugs” entry in the contextual menu. For large projects, this can take a while.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Selectively Suppressing Rules with FindBug Filters
- InhaltsvorschauLike Checkstyle (see ), FindBugs lets you define rules indicating which rules should be used or excluded in particular cases, such as in a particular class or method. A FindBugs filter works by combining one or more filter clauses, such as Class, BugCode, Method, and Priority. Matching cases may be included or ignored, depending on how the filter file is used. In most cases, you use a filter file to ignore specific issues.Let’s look at some examples. The following filter will match DE (method might drop exception) and EI (method may expose internal representation) in the com.mycompany.example.MyClass class:
<FindBugsFilter> <Match> <Class name="com.mycompany.example.MyClass"/ > <BugCode name="DE, EI"/ > </Match> </FindBugsFilter>More precisely, we can go down to the method level. The following filter will match these issues, but only in the processData() method:<FindBugsFilter> <Match> <Class name="com.mycompany.example.MyClass" /> <Method name="processData" /> <BugCode name="DE, EI" /> </Match> </FindBugsFilter>Or, if there are several methods that need to match, you can use the Or clause:<FindBugsFilter> <Match> <Class name="com.mycompany.example.MyClass" /> <Or> <Method name="collectData" /> <Method name="processData" /> <Or/> <BugCode name="DE, EI" /> </Match> </FindBugsFilter>You will often want to exclude certain rules for groups of classes, such as automatically-generated classes over which you have little control. To do this, you can use regular expressions in the class name expression. Regular expressions in FindBugs start with a “~”. The following deactivates the ICAST rule for all classes in the com.wakaleo.continuum.jabber package:<FindBugsFilter> <Match> <Class name="~com.wakaleo.continuum.jabber.*" /> <BugCode name="ICAST" /> </Match> </FindBugsFilter>In Eclipse, you can use these files in the FindBugs properties page, in the “Extended” tab (see ). Simply add the filter files to the Include or Exclude list, as .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using FindBugs Annotations
- InhaltsvorschauAnother interesting way to suppress FindBugs warnings is to use annotations. To use annotations, include the FindBugs annotations.jar file in your project classpath.One of the most useful of the FindBugs annotations is @SuppressWarnings. You can use this at several levels. For example, if you use it at the class level, it will suppress all FindBugs warnings for this class:
import edu.umd.cs.findbugs.annotations.SuppressWarnings; @SuppressWarnings public class JabberPublisher { ... }Alternatively, you can use it at a method level, which limits the scope of the suppression to the method:import edu.umd.cs.findbugs.annotations.SuppressWarnings; public class JabberPublisher { @SuppressWarnings void doSomething() { ... } }Finally, you can specify the exact rule you want to deactivate. In the following example, we deactivate the FindDeadLocalStores rule for the doSomething() method:import edu.umd.cs.findbugs.annotations.SuppressWarnings; public class JabberPublisher { @SuppressWarnings("DLS") void doSomething() { ... } }Other annotations let you help FindBugs detect potentially incorrect uses of your code. For example, in the following code sample, thegetClientByAccountNumber()method returnsnullif no matching client is found. Of course, you should mention this in the Javadoc, but you can also use the @CheckForNull annotation to ensure that anyone using this method checks to see if the method has returnednull:import edu.umd.cs.findbugs.annotations.CheckForNull; ... @CheckForNull void getClientByAccountNumber(String accNumber) throws HibernateException { ... }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using FindBugs in Ant
- InhaltsvorschauFindBugs also integrates well into the project build lifecycle. Project managers and quality assurance staff appreciate the ability of tools such as FindBugs to monitor and report on code quality. In this section, we will look at how to use the FindBugs Ant task to run checks and generate reports using Ant.To use FindBugs in Ant, you first need to download and install the FindBugs tool in a convenient directory. For example, you can install it into a directory called
toolsin your home directory, as shown here:$ cd ~/tools $ unzip findbugs-1.3.0.zip $ ln -s findbugs-1.3.0 findbugs
The easiest way to use the FindBugs Ant task is to copy the findbugs-ant.jar file into your Ant lib directory, as shown in this example:$ cd ~/tools/findbugs $ cp lib/findbugs-ant.jar /usr/share/ant/lib/
If you can’t (or don’t want to) modify your Ant installation, you can install FindBugs into a local directory and refer to this directory using a <classpath> directive, as shown here:<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" > <classpath> <fileset dir="${findbugs.home}/lib"> <include name="**/*.jar"/> </fileset> </classpath> </taskdef>In both cases, you will need to specify your FindBugs installation directory. You typically do this by defining a property pointing to the directory:<property name="findbugs.home" value="${user.home}/tools/findbugs" />Now you can write your FindBugs task. The following target will generate an XML report listing the issues detected by FindBugs:<target name="findbugs" depends="compile"> <findbugs home="${findbugs.home}" output="xml" failOnError="true" outputFile="findbugs-report.xml"> <class location="${build.classes.dir}" /> <auxClasspath refId="compile.classpath" /> <sourcePath path="src" /> </findbugs> </target>There are few mandatory attributes. You need to provide the path to your FindBugs installation in the home attribute. FindBugs operates on compiled bytecode, not on Java source code, so you also need to tell FindBugs where to find the compiled classes. TheEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using FindBugs in Maven
- InhaltsvorschauYou can also use FindBugs to generate bug reports in your Maven build process, via the Maven 2 FindBugs report plug-in. Like many other innovative Maven tools, this plug-in is hosted by Codehaus under the Mojo project. It is quite simple to use and integrates perfectly with the other reports in the Maven site.Integrating a FindBugs report into your Maven site can be as simple as adding the following plug-in to the reports section in your pom.xml file:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <configuration> <threshold>Normal</threshold> </configuration> </plugin>You can create the report by generating the entire maven site, using the mvn site command. This will produce a report similar to the one in .This plug-in takes most of the optional parameters as the Ant task (see ), which you specify in the <configuration> section: threshold, reportLevel, excludeFilterFile and includeFilterFile, effort, and omitVisitors are all supported.
Figure : A FindBugs report generated by Maven<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <configuration> <threshold>Normal</threshold> <effort>Max</effort> <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile> <omitVisitors>FindDeadLocalStores,UnreadFields</omitVisitors> </configuration> </plugin>One notable exception is the jvmArgs parameter, which is not supported in the current version. FindBugs is a memory-hungry beast, and you will probably need to increase the available memory if your project is of any size. If you need to provide more memory for the FindBugs processing, you will need to do it at a higher level, using the MAVEN_OPTS environment variable, as shown here:$ export MAVEN_OPTS=-Xmx512M $ mvn site
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauFindBugs is a useful and innovative static analysis tool that concentrates on finding potential bugs rather than coding style. Issues related to coding style and best practices aim at making code more readable and easier to maintain, mostly affecting the developers working on the application, and, indirectly, the organization that is paying them. Bugs, by contrast, affect the end user.Although there is some overlap with the rules of other static analysis tools such as Checkstyle and PMD in particular, FindBugs maintains a strong focus on only detecting potential bugs. As a result, FindBugs tends to raise fewer issues than these other tools; however, the issues raised should be taken seriously.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 24: Inspecting the Results—Semiautomated Code Review
- InhaltsvorschauCode reviews are possibility the single most efficient way to reduce defects and improve code quality. Simply put, code reviews involve manually inspecting source code for defects, often using a checklist of common errors to help focus the search. In addition, they are an effective way of improving your team’s development skills.As Cédric Beust points out in his interesting blog entry on the topic, there are two main approaches to code reviews, which he calls “blocking” code reviews and “nonblocking” code reviews. Blocking code reviews involve a more formal, rigid process in which code changes must be approved by a reviewer before they can be committed to the source code repository. Although this strategy should, theoretically at least, let less defects get into the code repository, it has the obvious disadvantage of potentially blocking a developer’s work until her code changes are reviewed.Nonblocking code reviews are less formal and more flexible. Developers submit their code changes to a reviewer, who will review them in due course. However, the developers don’t need to wait for the review before submitting their changes to version control. This more agile approach avoids blocking developer work, without necessarily compromising quality. Indeed, knowing that your code will be examined is a strong motivation for writing clearer, well-commented code.Whatever strategy you choose, you will need an underlying process that all team members must understand well and adopt. In general, if you want to introduce a new development process or best practice into an organization, it should be as simple as possible. As Einstein once said, “Things should be made as simple as possible—but no simpler.”Heeding Einstein’s advice, Jupiter, an open source collaborative Eclipse code review tool, uses a simple, lightweight code review process that is easy to learn and adopt. The result of a research project by the Collaborative Software Development Laboratory at the University of Hawaii, the Jupiter plug-in stores code reviews in an XML file format and maintains them in the project configuration management system alongside the source code.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introducing Jupiter—A Code Review Tool for Eclipse
- InhaltsvorschauCode reviews are possibility the single most efficient way to reduce defects and improve code quality. Simply put, code reviews involve manually inspecting source code for defects, often using a checklist of common errors to help focus the search. In addition, they are an effective way of improving your team’s development skills.As Cédric Beust points out in his interesting blog entry on the topic, there are two main approaches to code reviews, which he calls “blocking” code reviews and “nonblocking” code reviews. Blocking code reviews involve a more formal, rigid process in which code changes must be approved by a reviewer before they can be committed to the source code repository. Although this strategy should, theoretically at least, let less defects get into the code repository, it has the obvious disadvantage of potentially blocking a developer’s work until her code changes are reviewed.Nonblocking code reviews are less formal and more flexible. Developers submit their code changes to a reviewer, who will review them in due course. However, the developers don’t need to wait for the review before submitting their changes to version control. This more agile approach avoids blocking developer work, without necessarily compromising quality. Indeed, knowing that your code will be examined is a strong motivation for writing clearer, well-commented code.Whatever strategy you choose, you will need an underlying process that all team members must understand well and adopt. In general, if you want to introduce a new development process or best practice into an organization, it should be as simple as possible. As Einstein once said, “Things should be made as simple as possible—but no simpler.”Heeding Einstein’s advice, Jupiter, an open source collaborative Eclipse code review tool, uses a simple, lightweight code review process that is easy to learn and adopt. The result of a research project by the Collaborative Software Development Laboratory at the University of Hawaii, the Jupiter plug-in stores code reviews in an XML file format and maintains them in the project configuration management system alongside the source code.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Jupiter in Eclipse
- InhaltsvorschauJupiter is only available as an Eclipse plug-in. Like many Eclipse plug-ins, the easiest way to install Jupiter is to use the Remote Update site. Launch Eclipse and perform the following steps:The Jupiter plug-in adds a new perspective to your Eclipse workspace, called “Review,” which is the workbench of the Jupiter code review process. You can open this perspective by selecting “Window →Open Perspective → Other… → Review” (see ). This perspective comes with views designed to help you visualize and manage review issues—such as the “Review Table” and “Review Editor” views—visualize existing review issues and add new ones in the source code, and switch between different review process phases.
- Open the Help→Software Updates→Find and Install menu.
- Click Next, and choose New Remote Site.
- Enter the remote site URL (), and the site name (e.g., “Jupiter”).
- Make sure you have the Jupiter site checked in the “Sites to include in search” window, and click Finish. Then just go through the installation screens to install the plug-in.
Figure : The Jupiter Review perspectiveEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Understanding the Jupiter Code Review Process
- InhaltsvorschauThe code review process implemented in Jupiter is relatively simple, and it should suffice for most projects. In Jupiter, you conduct a code review in the following four stages:Throughout the whole process, the review files are stored and updated in the source code repository, providing a history of raised issues and how they where corrected.
- Configuration
- The review initiator sets up the review, defining a unique “Review ID” for this review, and specifying the files to be reviewed, who will review the code, and what issues can be raised. Depending on your organization, the review initiator could be the author, the team leader, or someone in QA.
- Individual code review
- Each reviewer examines the code individually, using a review checklist and raising issues as they encounter them. To create a new issue, you just place the cursor on the suspicious code, right-click, and select “Add Jupiter Issue.” Jupiter saves the issues you create in XML form directly in the project directory.
- Team review
- The review team (including the author) meet to discuss issues and decide on actions to take. This generally involves a face-to-face meeting, using Jupiter to help work through all the review issues.
- Rework
- The developer goes through the raised issues and fixes them.
For completeness, you should also add a preliminary phase, the personal code review, during which the developer reviews his own code.In the rest of this chapter, we will look at each of these stages in more detail.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conducting Personal Code Reviews
- InhaltsvorschauPersonal code reviews are a highly effective practice that plays an important part in the Software Engineering Institute’s Personal Software Process. A personal code review simply involves reading through the code and using the review checklist to look for errors.Using a review checklist is an important part of the review process. Reviews are much more efficient when you have precise goals in mind. With a review checklist, you actively hunt specific bugs, whereas, without one, you just wander through the code hoping to come across one.A review checklist contains defects or categories of defects that are known to have caused problems in the past. If you are already using tools like Checkstyle (see ) and PMD (see ), you don’t need to add any coding standards or duplicate any best practices that those other tools already have verified. Keep it short and simple to begin with, and then add new items as you come across them. And don’t forget to get everyone involved.One notable difference between the approach described here and the personal review process recommended in the Personal Software Process is that, in the latter, the individual review comes before compiling the code. One of the main arguments for this is that reviews conducted before compilation tend to be more thorough. Another reason is that if you let the tools find all the compilation errors, as well as the coding standards violations and other best practices errors, you will have a harder time tracking the number of issues raised.However, knowing how hard it is to put any sort of rigorous software development process into place, I believe in getting the most leverage out of your available tools and reserving human involvement for work that only humans can do. Indeed, if you want to introduce a new process into an organization, you should put as few obstacles as possible in the way and make the process as painless as possible.The following is a simple strategy for performing a personal code review:
- Obtain the code review checklist and display the class to be reviewed.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuration
- InhaltsvorschauTools and processes are all well and good, but, in practice, someone has to get the ball rolling. That person is the review initiator. Many people can play this role. It could be the owner of the code, the team leader or project manager, the chief architect, or even a QA person. It’s up to you to decide what suits your organization best.Keep in mind that many developers will consider code reviews a bit of a chore at best, and they will not come forth and volunteer their code for a code review. Others will delay the process as long as possible, waiting for their code to be as perfect as possible (for example, after the delivery date when they have more time), or just hoping that people will forget about them. If this is the case, the team leader or architect should take responsibility for initiating code reviews.A couple of other considerations may also weigh in favor of a centralized approach:
- Initiating a review involves assigning team members as reviewers.
- Initiating a review involves deciding which types of issues will be evaluated.
Many organizations will prefer to have these activities done centrally by one person (e.g., the project manager, the architect, or the lead developer).By contrast, for some projects, it may be more appropriate to have individual developers commit their own code. The advantages of this approach include the following:- When you notify reviewers that some code is ready to be reviewed, you should include a brief description of the purpose of the code (for new code) or the justification for the change (for changes to existing code). This is often best done by the developer.
- For any nontrivial classes, unit tests and unit test results should be submitted for review at the same time as the classes. Again, the developer is probably the best person to do this.
This approach works well if a single person is responsible for committing code to the next release version candidate. In open source projects, this person is often called the committer. In this sort of project organization, developers may commit their code to configuration management whenever necessary, but the committer is responsible for committing reviewed code to the release candidate code (which is often a new branch in the configuration management system). This also gives developers a good reason to have their code reviewed; if it isn’t reviewed, it won’t make it into the release version!Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Default Configuration Values
- InhaltsvorschauSetting up new reviews can be a repetitive process. Typically, for example, you will need to reenter the same team members again and again. To make life easier in this regard, Jupiter has a system-wide DEFAULT review configuration that can be used to set up default values for new review configurations. To modify this configuration, just open the Project Properties window, and select “Review.” This will list all the reviews stored for this project, and also a special, system-wide DEFAULT review. You can use this to set up default values, which will be used whenever you create new reviews in the future. You can set up any of the configuration properties that you would in an ordinary review (see ). Useful default configurations are:
- Place all your team members in the Reviewer list, then you will be able to pick and choose your review team for each new review from a predefined list.
- Setup yourself as the default reviewer (unless it’s always someone else, of course!). Remember, the default configuration is specific to your machine; it is not project- or organization-wide.
- Add custom issue categories in the “Items Entries” tab, if necessary.
Figure : Setting default configuration valuesEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Individual Reviews
- InhaltsvorschauThe second phase, and the first phase of actual peer reviewing, is the individual code review. This is the phase in which each reviewer examines the code on their own, at their own pace, and at their own convenience. This aspect of individual reviews makes it particularly appealing for developers who suffer from an acute allergy to meetings.The work involved in an individual code review is basically the same as that for a personal code review, except that the reviewer just raises the issues and does not fix the defects. Again, the use of a review checklist is very handy.In Jupiter, raising issues is straightforward. Make sure you have checked out the latest code from the configuration management system. Then select the Jupiter Perspective, and then select individual phase (see ).
Figure : Starting the individual reviewThis will open a window allowing you to choose the project, review, and user that you want to use for this review phase (see ).Now you can start the review. Go through the code looking for defects or issues. If you find something fishy, place the cursor on or simply select the suspicious-looking code, and select “Add Review Issue...” in the contextual menu. Next, specify the type and severity, and provide a summary and a description for this issue in the “Review Editor” window (see ). Don’t worry too much about the type: it’s just a tentative best guess for the moment, and you can change it during the team review phase.
Figure : Selecting the review
Figure : Describing an issueThe issue will also appear in the Review Table panel (see ), along with the other current issues. This table lets you add, delete, or edit issues; sort issues by severity or type; and jump directly to an issue in the source code.
Figure : The Review TableIn the source code window, recorded issues are marked by a purple marker (see ). If you move the cursor over them, the issue summary will appear.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Team Review
- InhaltsvorschauThe team review phase involves getting the review team (including the author) together to discuss the issues raised during the individual reviews. Typically, the team will work on one workstation, using an overhead projector to display the screen in a meeting, for example, or just work around the same machine if the review team is small enough. The team review involves going through all the issues raised and making a collective decision on the action to take.Team code reviews do have a lot going for them if not done to excess, and many organizations and projects report higher code quality and better team cohesion when using them.To start a team review, just select the team phase item in the Jupiter menu (see ). You then select the project, review, and reviewer ID in the Review ID Selection, as you did for the individual review phase (see ).Now the Review Table will display all the issues raised by the individual reviewers (see ). The details of the selected issue are displayed in the Review Editor (see ).
Figure : Initiating a team review
Figure : Conducting a team review
Figure : The team Review Editor windowYou should go through each issue, discuss it with the review team members, and come to an agreement on the following:- What should be done about this issue? Is it really an issue? This is recorded in the Resolution field, which can be one of the following:
- Valid needs fixing (a real issue that needs to be fixed)
- Valid fix later (a real issue that you won’t fix right away)
- Valid duplicate (such a real issue that it’s already been mentioned)
- Valid won’t fix (a real issue that you don’t want to fix)
- Invalid won’t fix (“it ain’t broke, don’t fix it!”)
- Unsure validity (needs further investigation)
- Who should this issue be assigned to (the Assigned To field)? By default, it is the code author, but it can be changed if someone with specialist knowledge has to intervene, for example.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Rework Phase
- InhaltsvorschauThe rework phase is the phase in which the developer goes through the raised issues and fixes the code accordingly. During the rework phase, Jupiter displays the list of issues that have been assigned to you so that you can go through them and correct them one-by-one.To start the rework phase, select rework phase in the Jupiter menu (see ). As for the other phases, you have to select the project and review what you want to work on, and specify your user ID. The Review Table will contain a list of issues that have been assigned to you.The details of the currently selected issue are displayed in the Review Editor (see ). When you fix a defect, you update the issue status field to Resolved. You may also want to add some details of your fix in the Revision field. When you’ve , you can move on to the next issue. Resolved issues will automatically disappear from the Review Table.
Figure : Initiating the rework phaseOnce the rework is finished, the reviewers should verify the corrections. If they are satisfied, they can approve a correction and close it (Closed status). If not, they can reopen it for further work (Reopened status). Jupiter makes it easy to see the state of all review issues by letting you turn off the filter on the Review Table (see ).
Figure : Reworking issues
Figure : Deactivating the filter in the review tableEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Jupiter Behind the Scenes
- InhaltsvorschauSometimes it can be useful to know a little about how Jupiter works behind the scenes. Jupiter stores review data in XML files, in a directory of your choosing (the default is a directory called “review” in the project base directory. Each reviewer has their own review file which they share with other team members via the version control system. This way, reviewers don’t get in each other’s way during the review process.The following listing shows a simple review file:
<Review id="review-1"> <ReviewIssue id="F86M91IY"> <ReviewIssueMeta> <CreationDate format="yyyy-MM-dd :: HH:mm:ss:SSS z"> 2007-10-25 :: 14:48:10:618 NZDT</CreationDate> <LastModificationDate format="yyyy-MM-dd :: HH:mm:ss:SSS z"> 2007-10-25 :: 14:48:35:024 NZDT</LastModificationDate> </ReviewIssueMeta> <ReviewerId>george</ReviewerId> <AssignedTo>john</AssignedTo> <File line="28">src/main/java/com/equinox/jpt/modelplanes/core/domain /ModelPlane.java</File> <Type>item.type.label.optimization</Type> <Severity>item.severity.label.normal</Severity> <Summary>Sub-optimal query</Summary> <Description>Could cause performance issues</Description> <Annotation /> <Revision /> <Resolution>item.label.unset</Resolution> <Status>item.status.label.open</Status> </ReviewIssue> </Review>This review file contains a single raised issue. Note that the line number is recorded in the entry for the review issue. The Jupiter plug-in is smart enough to keep tabs on what Eclipse is doing: if any file under review is modified, the line numbers in the review file will automatically be updated to correspond. This also applies for other reviewers who update their local copies of both the source code and the review file; if any modifications are done within Eclipse, the review file will be updated accordingly.However, if the file is changed outside of Eclipse, or if two file versions are merged, the review files may get out of synch with the source code files, and Jupiter may get a little confused about the line numbers. For this reason, it is wise to make your review comments clear enough not to rely absolutely on the line number.”Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauJupiter is an innovative and flexible tool that helps automate peer code reviews and track issues. Until recently, it was quite unique in this domain. Of late, however, it does have a commercial competitor, called Crucible, from Atlassian. Crucible is a new tool that also provides good support for online code reviews, and not surprisingly, integrates well with JIRA and the other Atlassian toolset.Tools like Jupiter are never sufficient themselves to improve code quality; you also need a defined development process and, more importantly, team and management buy-in. Nevertheless, Jupiter is a valuable process streamliner. If you practice code reviews, or if you would like to, you should definitely try it out.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 25: Sharpen Your Focus with Mylyn
- InhaltsvorschauContributed by: Keith CoughtreyToday’s development projects, and the environments we use to build them, are increasingly rich and complex, and informative. However, in practical terms, there is a limit to the amount of information human beings can deal with simultaneously. Mylyn is an innovative Eclipse plug-in that enhances developer productivity by providing integrated task management and a contextual view of the tasks you are working on.Mylyn benefits developers in two ways. The first provides a convenient integration with issue management systems such as Trac (see ), Bugzilla (see ), and JIRA, allowing you to view and manage tasks held in your issue tracker from within the IDE. This in itself is appreciable, but Mylyn goes much further than that. Mylyn also provides a way to hide much of the detail of your projects that is irrelevant to the task at hand. It is this second aspect, called context management, that provides a real increase in developer productivity because it cuts down on the amount of navigating, searching, and scanning through trees, lists, and code looking for the parts that are of interest when working on a particular task—whether that be a bug fix or the development of a new feature. Indeed, Mylyn is based on the observation that when you work on a particular development task, you actually need only to look at and manipulate a relatively small number of files. By masking out irrelevant information, Mylyn lets you focus exclusively on the files that need to be modified.Also, because Mylyn maintains a separate context for each task, when you return to a task that was worked on previously, you are immediately presented with those parts of you project that were considered relevant at the time. Because the context can move with the task as it is assigned to different members of the team, it may be that when you open a task you see those parts of the code that a previous developer worked on when he or she looked at the task. This can be a real head start or a great memory jogger.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introduction to Mylyn
- InhaltsvorschauContributed by: Keith CoughtreyToday’s development projects, and the environments we use to build them, are increasingly rich and complex, and informative. However, in practical terms, there is a limit to the amount of information human beings can deal with simultaneously. Mylyn is an innovative Eclipse plug-in that enhances developer productivity by providing integrated task management and a contextual view of the tasks you are working on.Mylyn benefits developers in two ways. The first provides a convenient integration with issue management systems such as Trac (see ), Bugzilla (see ), and JIRA, allowing you to view and manage tasks held in your issue tracker from within the IDE. This in itself is appreciable, but Mylyn goes much further than that. Mylyn also provides a way to hide much of the detail of your projects that is irrelevant to the task at hand. It is this second aspect, called context management, that provides a real increase in developer productivity because it cuts down on the amount of navigating, searching, and scanning through trees, lists, and code looking for the parts that are of interest when working on a particular task—whether that be a bug fix or the development of a new feature. Indeed, Mylyn is based on the observation that when you work on a particular development task, you actually need only to look at and manipulate a relatively small number of files. By masking out irrelevant information, Mylyn lets you focus exclusively on the files that need to be modified.Also, because Mylyn maintains a separate context for each task, when you return to a task that was worked on previously, you are immediately presented with those parts of you project that were considered relevant at the time. Because the context can move with the task as it is assigned to different members of the team, it may be that when you open a task you see those parts of the code that a previous developer worked on when he or she looked at the task. This can be a real head start or a great memory jogger.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Mylyn
- InhaltsvorschauMylyn comes bundled with Eclipse 3.3 and later versions. You can update the plug-in and install extra features using the Eclipse Remote Install feature. Open the “Find and Install” option from the Eclipse help menu, select the “Search for new features to install” and enter the URL of the update site. See Figure 25-1.Next you select the features you want to install. It’s probably best to include everything, but you can leave out any connectors you don’t intend to use.Even if you do have a more recent version of Eclipse with Mylyn integrated, be sure to check out the optional connectors and the Mylyn Extras update site. Here you can get useful additional connectors to other Issue Management systems such as Trac and Jira, or experiment with integration with the XPlanner Agile project management tool (see ).
Figure : Installing the Mylyn plug-in
Figure : The Mylyn extrasEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tracking Tasks and Issues
- InhaltsvorschauTasks and issues can come from many sources. To-do lists are a well known and widely used technique for enhancing personal productivity. In a development project, developers can also define and record tasks as an effective way of organizing their work. QA staff will raise issues during testing: missing features, bugs to be fixed, and so on. Mylyn allows you to track the progress of tasks that can either be stored locally in your workspace or stored in the repository of your favorite issue tracker. Mylyn provides connectors for Bugzilla (see ), Trac (see ), and JIRA, among .Indeed, Mylyn is an excellent, lightweight task management system in its own right. It provides you with a convenient, centralized view of all your tasks, both the ones that you assign yourself (your own “to-do” list) and issues raised by other people. You can also keep an eye on tasks assigned to other people that you may need to follow up on. And you can do all this without having to leave your development environment!To create a new local task, you simply click on the New Task toolbar button of Mylyn’s “Task List” view (see ). Then you can enter a summary of the task, set a priority, add an estimate, and schedule a date on which the work will be done. These tasks are not visible to other users, so this is a good way to organize your work on a personal level.
Figure : Adding a Mylyn taskThe Mylyn “Task List” view provides a few nice features that help you stay focused on the most important jobs at hand. Completed tasks are barred, and the current active task (see ) is shown in bold. If you have a lot of tasks (and you generally will!), a good trick is to focus on the tasks due in the current week. You can do this using the “Focus on Workweek” button on the Task List toolbar, or using the popdown menu (see ). You can also filter out completed tasks or tasks below a certain priority.
Figure : The Mylyn task listMylyn also offers some simple but effective schedule management features. You can use the tasks to manage your time, rescheduling things if necessary as priorities change. When a task is overdue, it appears in red in the task list (see ), and a popup window appears on your desktop to remind you (just in case). If you do need to it, just use the “Schedule” option in the contextual menu (see ). With a simple right-click, you can indicate that a task is complete or defer it to a later date.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Interacting with Task Repositories
- InhaltsvorschauMylyn provides sophisticated integration with many issue tracking systems such as Bugzilla (see ), Trac (see ), and JIRA. To integrate with JIRA, JIRA needs to be configured to accept remote API calls (make sure the “Accept remote API calls” option in the General Configuration screen is activated).
Figure : Progress bars give an idea of how much planned work has been completedTo integrate with your issue tracking system, you need to set up a task repository. Open the Mylyn Task repositories view and click on the toolbar button to create a new repository connection (see ). Select the repository type, click Next, then enter the server details. Choose Validate Settings and then Finish. You can set up as many task repositories as you want, which is useful if you are working on several projects that use different issue tracking systems.At the time of this writing, the server drop-down has an example entry for both the Bugzilla and Trac repository types. It is worth exploring these repositories to get a feel for how the integration .
Figure : Adding a new task repositoryOnce you have set up a task repository, you can retrieve tasks and issues by querying the repository. For example, you might want to create a query to bring back all the issues assigned to you. You can create a new query from the “Task List” view using the “New→Query” menu option (see ).The attributes displayed depend on the repository type but, as you can see, they give all the options you need to filter out a subset of the repository tasks that are of interest to you.
Figure : Querying the issue management systemOnce you have created the query, it will appear in your “Task List” view (see ). You can double-click on these tasks to view the details page in the web browser embedded within Eclipse. This is another nice thing about Mylyn: when you view or modify repository tasks, you use the same web interface as you would normally use with your issue tracking system. You just do it from within Eclipse.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Focusing on a Task with Context Management
- InhaltsvorschauThe objective here is to reduce the information overload you typically get when working on projects in Eclipse.Without Mylyn, you quickly find that the Package Explorer and Navigator have hundreds or perhaps thousands of entries. The outline view of a complex class may show dozens of properties and methods and you often end up with far too many code editors open to display. Also, when code assist pops up, it often contains a long list of possible selections.Generally, only a small subset of all this information is relevant to the task in hand. For example, the class you are working on may contain many methods that are stable and not of interest in the particular context and of course the same goes for the Outline view.With Mylyn all you need to do is to activate a task (by clicking a radio button in the task list) before you start working on it and from then on, your activity starts to build up the context. When you activate a task, the package explorer view will automatically be filtered to display only the objects deemed of interest for this particular task.At the start, your context is empty and the various views such as the Package Explorer show what appears to be an empty project. The easiest way to add content is to use the Open Type dialog (Ctrl+Shift+T) to search for the object of interest. If you then select a class to open, that class is added to your context because you have shown an interest in it.Initially, all of the methods in the class you have opened will be folded on the basis that none of them are of interest. However, as you click on methods they unfold and are added to your context, appearing in the Package Explorer and the Outline views.The more that you work with elements, the more interesting they are presumed to be. Eventually they become landmarked, indicating that they are a key part of the context of this task. Mylyn adds a toggle button to the Package Explorer, Navigator, and Outline views that allows you to apply the task focus in two different ways. When the toggle is set to focus on the active task, these views show only those items that are of interest. However, when the task focus is toggled off, the views present everything that they would without Mylyn, but some decoration is added to make the items of interest stand out. By default, all uninteresting items are shown in gray, interesting items in black, and landmarks in bold.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Using the Eclipse Change Sets
- InhaltsvorschauIn Eclipse, change sets allow you to organize resources that you have modified into logical groups before updating them in your source code repository. Eclipse supports this mechanism for CVS and Subversion. In CVS, all of the changes are submitted with the same comment message. In Subversion, where change sets are supported natively, all of the modifications in the change set are submitted as a single transaction.Change sets in Eclipse are good, but they are complicated to manage manually in any but the most trivial of cases. However, Mylyn can help you manage your change sets automatically. Whenever you activate a task, a new change set will be created for that task. Any files you modify while working on that task will be added to this change set automatically.You can manage the Mylyn change set in the Eclipse Synchronize view (“Team→Synchronize”). To enable change set support, ensure the “Show Change Sets” button is set on the Synchronize view. Here, you can view the modified files that currently make up your change set, or commit or revert your changes. And, if you really need to, you can always manually add other modified files to the Mylyn change set using the contextual menu (see ).Another advantage of using the Mylyn change sets is that the commit message is automatically initialized with a sensible message based on the title and status of your active task (see ). This makes it easier to relate source code modifications to issues in a consistent manner. If you are using Trac, it is also possible to configure Trac to automatically append this message to the issue whenever the changes are committed (see ).
Figure : Eclipse change sets with Mylyn
Figure : Committing a Mylyn change setIt can be useful to customize the message format to suit your environment. For example, if you are using Subversion and Trac, you might want to configure Subversion to update the Trac issue based on the Subversion message (see ). In this case, you can customize the message so that the Subversion hook will identify the Trac issue in the message, as shown in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Sharing Context with Other Developers
- InhaltsvorschauThe Mylyn context is a powerful tool that enables you to get up to speed very quickly when you reactivate a task. It also helps other developers who may have to pick up a task from where you left off. Providing them with the context you so painstakingly created can be a valuable aid. The developer can immediately see what files you were modifying, what tests you where running, and so forth. This allows him to immediately focus on the correct trouble spots, rather than having to search for them himself.In Mylyn, you can share your contexts with other developers. Mylyn lets you attach the current context to a task and record this context as an attachment to the ticket in the issue management system (at the time of this writing, this only worked for Bugzilla task repositories). You do this from the “Context” tab, using the “Attach context...” link. Subsequently, other developers can click on “Retrieve context...” to download and activate this context in their own Eclipse environment.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Conclusion
- InhaltsvorschauThe task and context management features of Mylyn work together to help you focus your effort on the task at hand, making everything relevant easier to find and hiding the irrelevant. When you factor in the ability to share contexts with other teams members, these features make the plug-in a worthwhile addition to your Eclipse .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 26: Monitoring Build Statistics
- InhaltsvorschauThere are many open source reporting tools available that can give you useful information about your project. Checkstyle, PMD, and FindBugs analyze the project source code, looking for code that fails to respect coding conventions or contains errors. reports on test coverage, giving you an idea of how well your code has been tested.All of these tools can provide detailed information about one particular aspect of your project at a particular point in time. However, it can also be useful to see how these statistics evolve over the lifetime of a project. Does the average code coverage increase or decrease? How does the number of errors detected by PMD and FindBugs evolve over time. How much code has been added to the project over the last month? For this type of query, a snapshot analysis of your project will not be enough—you need to study the data over time.There are many tools that can help you in this area. QALab collects data from these tools and reports on the evolution of these statistics over time. For example, you can visualize the evolution of code quality and test coverage statistics over the life of the project. In this chapter, we will look at some tools that you can use to collect and display this sort of time-related statistical data.QALab collects data from tools such as static analysis results and code coverage, and reports on the evolution of these statistics over time. For example, you can visualize the evolution of code quality and test coverage statistics over the life of the project.QALab works by extracting key information from the reports generated by the other tools, and storing this data in a single file called qalab.xml. Because QALab deals with statistical trends and not individual violations, only summary-level information is recorded. For QALab to work effectively, you need to generate data for QALab on a regular basis—such as during nightly builds or during the Continuous Build process.QALab generates two types of reports: Charts and Movers. The charts track the evolution of data since the start of the project (or since QALab was installed). You can see an example of a QALab chart in . The movers allow you to see at a glance what has changed since the last QALab reports where generated. For example, rather than displaying Checkstyle results since the start of the project, movers focus on how many more (or less) violations there were today compared to yesterday.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introduction
- InhaltsvorschauThere are many open source reporting tools available that can give you useful information about your project. Checkstyle, PMD, and FindBugs analyze the project source code, looking for code that fails to respect coding conventions or contains errors. reports on test coverage, giving you an idea of how well your code has been tested.All of these tools can provide detailed information about one particular aspect of your project at a particular point in time. However, it can also be useful to see how these statistics evolve over the lifetime of a project. Does the average code coverage increase or decrease? How does the number of errors detected by PMD and FindBugs evolve over time. How much code has been added to the project over the last month? For this type of query, a snapshot analysis of your project will not be enough—you need to study the data over time.There are many tools that can help you in this area. QALab collects data from these tools and reports on the evolution of these statistics over time. For example, you can visualize the evolution of code quality and test coverage statistics over the life of the project. In this chapter, we will look at some tools that you can use to collect and display this sort of time-related statistical data.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- QALab
- InhaltsvorschauQALab collects data from tools such as static analysis results and code coverage, and reports on the evolution of these statistics over time. For example, you can visualize the evolution of code quality and test coverage statistics over the life of the project.QALab works by extracting key information from the reports generated by the other tools, and storing this data in a single file called qalab.xml. Because QALab deals with statistical trends and not individual violations, only summary-level information is recorded. For QALab to work effectively, you need to generate data for QALab on a regular basis—such as during nightly builds or during the Continuous Build process.QALab generates two types of reports: Charts and Movers. The charts track the evolution of data since the start of the project (or since QALab was installed). You can see an example of a QALab chart in . The movers allow you to see at a glance what has changed since the last QALab reports where generated. For example, rather than displaying Checkstyle results since the start of the project, movers focus on how many more (or less) violations there were today compared to yesterday.The chart graph is quite powerful in a number of ways. If you click the graph, you will obtain a list of all the current files. Clicking any one of these files will display a graph containing the historical data just for this file. QALab integrates well with Ant and Maven.
Figure : QALab generates graphs of quality-related statistics such as Cobertura code coverage, and Checkstyle, PMD, and FindBugs violations over timeBefore you can use QALab in Ant, you need to download the latest QALab jar and the Maven QALab plug-in jar —as well as its dependencies (jcommon, jfreechart, xerces, and xercesImpl). The dependencies are not provided in the QALab download, so you have to hunt them down yourself. The JCommon and JFreeChart libraries are available at the JFreeChart site (). If you have a lot of development machines to install, it may be easier to define a separate Ant build file (called, for ,Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Source Code Management Metrics with StatSCM
- InhaltsvorschauIt is often useful (or merely satisfying) to know how much code has been added to a project over a certain period of time. In Maven 2, the StatSCM plug-in lets you do just that. Written by Doug Culnane, StatSCM is a wrapper on top of two other tools—StatCVS and StatSVN—which we will look at further on.StatSCM is capable of generating an abundance of statistical information about your project, including:
- How fast the source code base has been growing
- Who is actively working on this project
- How much code each developer has been contributing, and how active they have been
- What release tags have been made
- Which files are being modified the most often
StatSVN also provides a graphical view of your repository structure.StatSCM is easy to set up and to use. First, make sure the <scm> section (see ” in ) of your POM file is correct. StatSCM uses this information to find the source code repository and study the logfiles. For Subversion, it also counts the number of lines of code in each changeset, which can take quite a while for a big repository.StatSCM is not in the standard Maven repository, so you need to add the following plug-in repositories to your POM file:<pluginRepositories> <pluginRepository> <id>stat-scm-sourceforge</id> <url>http://stat-scm.sourceforge.net/maven2</url> </pluginRepository> <pluginRepository> <id>stat-scm-sourceforge-snapshot</id> <url>http://stat-scm.sourceforge.net/maven2-snapshots</url> </pluginRepository> </pluginRepositories>Then add the stat-scm report to your list of reports:<reporting> <plugins> ... <plugin> <groupId>net.sf</groupId> <artifactId>stat-scm</artifactId> </plugin> ... </plugins> </reporting>Now, when you generate your Maven site, you will be able to browse through a very complete set of interactive reports containing statistical data about the evolution and activity of your project, from the point of view of its source code repository, during the life of the project (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Statistics in Ant with StatSVN
- InhaltsvorschauThe current version of StatSCM provides Maven 2 users with powerful statistical reporting functionalities about Subversion or CVS repository activity. If you are using Ant (see ) and Subversion (see ), you can obtain these detailed statistics by using the underlying StatSVN tool directly. StatSVN is a powerful tool designed to be run either directly from the command line, or using the built-in Ant Task. Here, we will concentrate on how to use StatSVN from within Ant.StatSVN is distributed as a simple JAR file. To install it, simply download the latest version from the StatSVN web site and save this JAR file in an appropriate place on your disk. This JAR file also contains the StatSVN Ant task that we will be using. You can either copy the JAR file into your Ant
libdirectory, or, alternatively, refer to the JAR in your <taskdef> declaration, as shown here:<taskdef name="statsvn" classname="net.sf.statsvn.ant.StatSvnTask" classpath="${statsvn.home}/statsvn.jar" />Although StatSVN is powerful, it is also fairly low level and involves working directly with your Subversion logfiles, which you must extract yourself. StatSVN works with the Subversion log messages in XML form. From the command line, you would need to execute the svn log command, as shown here:$ svn log -v --xml http://svnserver.mycompany.com/svn/repos/myproject/trunk
In an ideal world, you would be able to use the SvnAnt library (see ) to do this. However, at the time of this writing, the svn log command was not supported in SvnAnt, so you need to use the Ant <exec> task to invoke the svn command at the OS level. From within Ant, you could define a target called “svn.log” to do this:<property name="svnUrl" value="http://svnserver.mycompany.com/svn/repos /myproject/trunk/" /> <target name="svn.log"> <exec executable="svn" output="${basedir}/target/svn.log"> <arg value="log"/> <arg value="-v"/> <arg value="--xml"/> <arg value="${svnUrl}"/> </exec> </target>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 27: Bugzilla
- InhaltsvorschauBugzilla is probably the most well-known of the open source issue tracking solutions, and is used by many high-profile open source projects such as Mozilla, Apache, and Eclipse. It is a mature, high-performance, feature-rich open source issue management solution well adapted for use in very large projects. It has been adopted by a large number of organizations and projects, both in the open source world and for commercial products. On the downside, it has a fairly well-earned reputation of being hard to install and to maintain, and its default user interface—with its fast, lightweight, no-frills screens—is possibly one of the ugliest and most unfriendly around. In this chapter, we will look at how to install, use, and customize Bugzilla.Bugzilla has an arguably justifiable reputation for being fairly hard to install.Bugzilla is typically installed in a Unix or Linux environment, although the more recent versions work fine on Windows as well. Bugzilla runs on Perl, and uses either MySQL or PostgreSQL as a backing database. You also need a web server: Apache is the typical choice. Installation is done from the command line and basically involves installing all the necessary Perl modules, setting up the database, and scheduling external Perl scripts to collect data for bug graphs and to send notifications. Notifications are done by email, so you also need a mail server. The installation process is long and involved, often full of surprises, and only the bravest and most intrepid will come through a full Bugzilla installation unscathed. Here, I will go through the main steps, and try to point out some of the more common pitfalls.This section concentrates on how to install Bugzilla into a Unix environment. On a Windows machine, the procedure is a little different; you can find a good tutorial on the Bugzilla web site.Bugzilla uses Perl as its scripting language, so you will need to have Perl installed on your machine. Most Unix/Linux distributions come with a recent version of Perl installed; if you have a doubt, runEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- An Introduction to Bugzilla
- InhaltsvorschauBugzilla is probably the most well-known of the open source issue tracking solutions, and is used by many high-profile open source projects such as Mozilla, Apache, and Eclipse. It is a mature, high-performance, feature-rich open source issue management solution well adapted for use in very large projects. It has been adopted by a large number of organizations and projects, both in the open source world and for commercial products. On the downside, it has a fairly well-earned reputation of being hard to install and to maintain, and its default user interface—with its fast, lightweight, no-frills screens—is possibly one of the ugliest and most unfriendly around. In this chapter, we will look at how to install, use, and customize Bugzilla.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Installing Bugzilla
- InhaltsvorschauBugzilla has an arguably justifiable reputation for being fairly hard to install.Bugzilla is typically installed in a Unix or Linux environment, although the more recent versions work fine on Windows as well. Bugzilla runs on Perl, and uses either MySQL or PostgreSQL as a backing database. You also need a web server: Apache is the typical choice. Installation is done from the command line and basically involves installing all the necessary Perl modules, setting up the database, and scheduling external Perl scripts to collect data for bug graphs and to send notifications. Notifications are done by email, so you also need a mail server. The installation process is long and involved, often full of surprises, and only the bravest and most intrepid will come through a full Bugzilla installation unscathed. Here, I will go through the main steps, and try to point out some of the more common pitfalls.This section concentrates on how to install Bugzilla into a Unix environment. On a Windows machine, the procedure is a little different; you can find a good tutorial on the Bugzilla web site.Bugzilla uses Perl as its scripting language, so you will need to have Perl installed on your machine. Most Unix/Linux distributions come with a recent version of Perl installed; if you have a doubt, run perl --version from the command line:
$ perl --version This is perl, v5.8.8 built for i486-linux-gnu-thread-multi Copyright 1987-2006, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at http://www.perl.org/, the Perl Home Page.If for some reason you don’t have Perl on your machine (for example, if you happen to be running under Windows), you will need to download and install one of the binary distributions from the Perl web site.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Your Bugzilla Environment
- InhaltsvorschauOnce you get Bugzilla running, you will need to tailor it to your environment. Bugzilla asks you to define a few basic parameters the first time that you log in using your administration account (remember, the one we said not to forget at the start of the ?). You need to set up things like the email address of the person responsible for maintaining the installation, the base URL of your Bugzilla installation, and whether some or all of the site should be protected by encrypted SSL access.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Managing User Accounts
- InhaltsvorschauWhen it comes to user accounts, Bugzilla is very much based on an open source school of thought. By default, anyone can search and view bugs without needing to log in. You need to log in if you want to create or update bugs. Users can create their own accounts by clicking on the “New Account” link at the bottom of every screen. This is, in fact, the best and most convenient way to create new user accounts because users can manage their own email addresses and password details without having to bother the administrator. All a user needs to provide is a valid email address. New users created this way can freely search, create, and update bugs.This approach is fine for many projects, especially open source ones.Bugzilla provides a useful web interface for creating and administrating user accounts. Administrators can manage user accounts in the “Users” screen (see ). This screen lets you search and list existing user accounts, modify user account details, and create new user accounts. Creating user accounts from the administration screens is usually only done for testing purposes, as users are not automatically notified when their account is created.
Figure : Managing Bugzilla user accountsDefault users can search, create, and update bugs, but they are not authorized to do very much else. Other functionalities, such as managing products and components, configuring periodic email notifications (known in Bugzilla terms as “whining”), and, indeed, managing users and user groups, are reserved for authorized users only. You can do this in the “Edit user” screen (see ). This screen lets you modify the user’s login details and also authorize the user to access these other functionalities.Bugzilla is fundamentally designed to be an open, public-facing issue management system, and any user can try to create his or her own account. By default, Bugzilla requires (and is happy with) a full, correctly formed email address (so it won’t accept “jill” or “joe@myserver,” for example). If your project requires more security, you can restrict access by modifying the
Figure : Managing user account details in BugzillaEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Restricting Access Using User Groups
- InhaltsvorschauBugs in Bugzilla are, by default, visible to any user. However, you can use groups to limit the visibility of certain bugs and products to certain users. For example, you may want to limit access to bugs in a particular project to members of the project team only. In this section, we will go through how to do this in Bugzilla using user groups.The first thing you need to do is set up a new group. You do this in the “Groups” page (see ). This group will appear in the group lists alongside the special System groups such as admin and editbugs.Once you have added a group for your project, you need to place the team members in this group. Unfortunately, you have to do this individually for each user in the user details page (see ).
Figure : Adding a new user group in BugzillaWhen you have assigned all of your team members to the new group, you can restrict access to a product in the “Edit Group Controls” page, which you can access by clicking the “Edit Group Access Controls” link in the product details page (see ). This screen contains a list of all the user-defined groups. Each column lets you restrict a different functionality to members of certain groups. The first two (Entry and CanEdit) restrict access to bugs for this project, whereas the final three (editcomponents, canconfirm, and editbugs) widen access, letting in users who otherwise wouldn’t be able to view or edit the project bugs.- Entry
- If you check the “Entry” column for a particular group, only members of this group will be able to enter bugs against this product.
- CanEdit
- Checking “CanEdit” will mean that only team members will be able to modify bugs for this project—other users will have read-only access.
- editcomponents
- If you check this option, users who belong to a group with editcomponents privileges will be able to edit the product components, milestones, and versions, even if they don’t belong to the project group.
- canconfirm
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuring a Product
- InhaltsvorschauIt is fairly easy to set up a new project in Bugzilla. The main goal of a software development project is to create (or modify) a software application of some kind. In Bugzilla, a software application is known as a product. You manage products in the Bugzilla “Products” screen, which you can access via a link in any screen footer when logged in as an administrator (see ).In Bugzilla, products are made up of components. You can define the components of a product by clicking on the “Edit components” link on the product details page. This will display a list of components for this product, letting you modify or delete existing components, or create new ones.
Figure : Managing products in BugzillaA product cannot exist by itself; you need to give it at least one component. While you’re at it, however, you might as well add several. You use components to decompose your project into smaller chunks. Exactly how you do this will depend on your project and on your organization; Bugzilla doesn’t really care. Typically, a component will be small enough to be the responsibility of a single developer, or have a readily identifiable development leader who coordinates work on the component. In Bugzilla, each component is the responsibility of a particular person, defined in the “Default Assignee” field. A component should be meaningful enough to an end user (or at least a QA person) to be able to log bugs against it with some degree of reliability. You can also use the “Default CC List” to define a list of users who will always be notified when bugs logged against this component are modified.Some examples of what a component might be in your project are:- A subproject within the main development project
- A software module
- A particular feature or functionality
- The implementation of a particular use case or user story
In a similar way, you can also define versions for your product using the “Edit versions” link. A version is a released product in which bugs are found, so you usually create new version entries in Bugzilla as they are released for testing. During testing, when a bug is found, you specify the version being tested when the bug was found.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tracking Progress with Milestones
- InhaltsvorschauMilestones are an excellent tool for project tracking and prioritization. If you are using an iterative or agile development approach, you can set up a milestone for each iteration. Bugs can then be associated with a particular milestone, making it easier for the development team to know where to focus its development efforts, and making it easier for the testing team to know what it is meant to be testing.Bugzilla has optional support for milestones. They are not available by default: you need to activate them by setting the “usetargetmilestone” option (in the “Bug Fields” section of the “Parameters” screen) to “On.” When you do this, you can create milestones for a particular project (see ), define the target milestone for a particular bug, and view the list of bug corrections planned for a given milestone.
Figure : Managing milestones in BugzillaEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing Groups of Products with Classifications
- InhaltsvorschauSometimes, it is useful to be able to associate several different but related products. You may want to regroup several distinct products that belong to the same program of work, a suite of software applications, or products that are related in some other way. Classifications let you group related products. By default, classifications are deactivated. To enable them, you need to activate the “useclassification” option (which is in the “Bug Fields” section of the “Parameters” screen). Once you have activated , an administrator will be able to manage them using the “Classifications” link in any screen footer (see ).
Figure : Managing project classificationsSetting up a classification simply involves giving it a name and associating some projects with it. Once you have at least two classifications set up with some projects in them, Bugzilla will ask you to choose a classification when you enter a bug (see ). You will also be able to use the classification field as a search criteria in the Advanced Search screen.
Figure : Selecting a classification during bug entryEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Searching for Bugs
- InhaltsvorschauBugzilla is designed to be a robust, high-performance issue tracking system, capable of handling databases with hundreds of thousands of bugs. In a database with that many bugs, you obviously need some good search functionality to find the ones you are looking for.Bugzilla provides several ways to search the database, each appropriate in different situations. The simplest way to look for a bug is to use the search field in the page header. This provides a quick-and-easy full-text search across the entire database. You can also enter a numerical bug id, in which case Bugzilla will take you directly to the bug details.You can do more elaborate searches in the Search screen (accessible using the “Search” link in the header of any page). From this screen, you can choose between two types of searches. Probably the most useful is the “Find a Specific Bug” tab. This tab provides a simple, full-text search (see ), possibly filtering by status or for a particular product.
Figure : Selecting a classification during bug entryThe “Advanced Search” tab is a much more complicated beast that lets you search on virtually any field in the database.Search results are displayed as a table (see ). Note that Bugzilla doesn’t know about paging results, so if your query returns 4,000 bugs, you will get 4,000 entries on the one screen.The results screen has a few nice additional features to it. The “Long Format” button displays a list of detailed information for each bug, including all the principal fields, the description, and any comments. This is especially useful for preparing those bug review meetings. You can also subscribe to a query in the form of an RSS feed, which is useful if you want to keep tabs on a certain category’s issues (such as issues for the product you are working on) without having to continually open Bugzilla and run the search.Another useful feature is the “iCalendar” button. It lets you download the list of bugs in iCalendar format so that you can import them as tasks in your agenda.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Creating a New Bug
- InhaltsvorschauAlthough the interface is rudimentary and lacks many of the niceties of more recent Web sites, entering a new issue in Bugzilla is relatively straightforward. After selecting the buggy product, you arrive at the bug details screen (see ). Out of the numerous fields that you’ll see, only the Summary and Component fields are actually mandatory. You will usually also provide other details, such as a version number (the version of the product in which the bug was found), severity of bug, platform, a target milestone, and so on. You can assign a bug directly to a developer if you know who you are working with, or just wait for it to be picked up by some willing soul. If you can’t remember the definitions of the various severity levels or how to use a particular field, convenient hyperlinks near each field let you display the appropriate help pages. You can also use attachments to add screen shots or the like.
Figure : Entering a new issueEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Lifecycle of a Bugzilla Bug
- InhaltsvorschauBugzilla supports a fairly complete, albeit hardcoded, workflow model, illustrated in .Bugs begin life when a tester or user enters them in Bugzilla. Newly created bugs can be NEW, ASSIGNED, or UNCONFIRMED.
Figure : The lifecycle of a bug in BugzillaIn simple terms, an UNCONFIRMED bug is one that needs to be reproduced by QA before it can be set to NEW and assigned to a developer. The UNCONFIRMED initial status is not available by default. If you want to allow UNCONFIRMED bugs, you need to activate the “usevotes” option in the “Bug Fields” section of the “Parameters” screen. Then you need to edit the product (see ) to make sure that the “Number of votes a bug in this product needs to automatically get out of the UNCONFIRMED state” is greater than zero, and also that the Maximum votes per person is greater than zero. When this is done, you will be able to create new bugs in the UNCONFIRMED status.A bug can go from UNCONFIRMED to NEW either by having people vote for it, or when it is manually confirmed by a QA person (or any member of the canconfirm group). The QA person may assign the bug to a developer using the “Assigned To” field. Note that, even when a bug is “assigned to” a developer, it can still be in the NEW state. It will remain in this state until the developer reviews the issue and accepts it (in which case its state will go to ASSIGNED), or reassigns it to another developer. In fact, the ASSIGNED state actually means that a developer has accepted the bug and is actively (at least, in theory!) working on the problem.Once the developer has finished working on an issue, he places it in the RESOLVED state. When he resolves a bug, he has to specify how it was resolved (see ). A bug can be resolved in several ways:- FIXED
- The bug was fixed and the correction submitted for testing by QA
- INVALID
- The developer does not think this issue is really a bug
- WONTFIX
- There is a bug, but it won’t be fixed
- LATER
- This bug will be fixed in a future version
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Scheduling Notifications (Whining)
- InhaltsvorschauBugzilla allows you to schedule notifications to be generated and sent to users on a regular basis. This process is known as whining. Whining basically involves scheduling predefined queries to be run at regular intervals or at specific times (for example, every Sunday). You can schedule any query you want; for example, you might want to send out the list of open bugs for a particular project to the project team every Sunday to remind them to prepare for the weekly status meeting.The first thing to do is to build and run the query in the Search screen. Then save the query results using the “Remember Search” button. Once you have saved your query, go to the “Whining” configuration page (see ). Here, you simply specify when the notifications should be generated and sent out, what query should be used, and to whom they should be send. You can send notifications to individuals and to groups; both can be useful in some circumstances.
Figure : Configuring WhiningOn the configuration side of things, the whining mechanism will only work if you run a PERL script called whine.pl at regular intervals (for example, every 15 minutes). You need to set this up outside of Bugzilla, for example, using cron.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Fields in Bugzilla
- InhaltsvorschauWhen you install an issue tracking system, there is often much debate about how to classify issues, particularly in terms of defining the various levels of priority and severity. Bugzilla lets you customize certain key fields such as Priority and Severity, as well as the environment-related fields such as “OS” and Hardware, via the “Field Values” link in the page footer. You can rename the existing values, add new ones, or delete unnecessary values (if they are not currently being used by any bugs).It is important to note that if you do change these values, the corresponding contextual help screens will still refer to the default Bugzilla values. If you want to change these as well (which you probably should), you need to update the fields.html.tmpl file, which you will find somewhere under the template directory (the default English version of this page can be found at template/en/default/pages/fields.html.tmpl). Another alternative is to provide definitions of the terms in the label itself, such as “Critical: The software crashes, hangs, or causes you to loose data.”Although Bugzilla already has more than enough fields for many users, there may be times when you would like to have just one extra bit of information. For example, you may want to know on what test platform the error occurred, or you may need to know some other detail about how or where the application was deployed. Catering to this need, Bugzilla allows you to define custom fields via the (you guessed it!) “Custom Fields” link in the page footer. Custom fields are easy to create: you just need to provide an internal field name (which must start with “cf_”), a description (which will be used as the field label in the bug details page), and some other details such as whether the field should be visible when you create a new bug, and whether the field should be used in notification emails. Custom fields can currently be either plain text fields or drop-down lists.When you create a custom field using a drop-down list, you can’t set up the values immediately. You need to create the custom field, then edit the legal values for this field (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Conclusion
- InhaltsvorschauBugzilla is a tried-and-true solution that will support very large projects and user bases. Its workflow features are more than sufficient for most organizations. On the downside, Bugzilla is particularly complicated to install and maintain, and the user interface won’t win any prizes for design or usability. Its reporting features will do the job, although they are not particularly user-friendly.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 28: Trac—Lightweight Project Management
- InhaltsvorschauTrac is a lightweight, open source issue tracking and project management tool that builds on Subversion () by adding flexible web-based issue tracking, wiki, and reporting functionalities. Its second-to-none integration with Subversion makes it an excellent choice for development teams who use Subversion. It imposes no particular methodology, and so allows a great deal of flexibility in the way it is used. Although it does not provide as wide a range of features as products like Bugzilla, Trac is steadily gaining popularity in the Subversion community. Indeed, many commercial Subversion hosting companies now offer Trac as part of their package deals.Trac provides a number of interesting features that make it a tool particularly well suited to small teams using lightweight, agile development processes. These include:
- A lightweight issue-tracking system, where issues can be entered and assigned to team members with a minimum of formality and effort
- An excellent web interface to your Subversion source code repository, allowing you to browse your source code and revisions, and keep tabs, via both the web site and RSS feeds, on what changes are happening in the repository
- A wiki-based project management tool, in which you can manage project milestones and iterations, assign tasks, and share documentation and ideas
But one of the nicest things about Trac is the way all of these features are integrated together. Using a wiki-style syntax, you can include references to Trac issues, tasks, or wiki pages in your Subversion commit messages, or refer to Subversion changesets from within Trac. These references are rendered as HTML links on the project web site. And a project timeline gives you an overall view of all activity on your project, making it easy to keep track of changes made to issues, tasks, and within the source code .In this chapter, we will discuss how to install and use Trac in your projects.Installing Trac is not a difficult task in itself, but the various dependencies can make things a little complicated, especially on a Windows machine. In addition, the Trac installation process tends to vary a great deal in its finer details from one system to another. In this chapter, we will cover the general steps and also a few tips to get you started. For more details, the best and most up-to-date reference remains the Trac web site itself (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - An Introduction to Trac
- InhaltsvorschauTrac is a lightweight, open source issue tracking and project management tool that builds on Subversion () by adding flexible web-based issue tracking, wiki, and reporting functionalities. Its second-to-none integration with Subversion makes it an excellent choice for development teams who use Subversion. It imposes no particular methodology, and so allows a great deal of flexibility in the way it is used. Although it does not provide as wide a range of features as products like Bugzilla, Trac is steadily gaining popularity in the Subversion community. Indeed, many commercial Subversion hosting companies now offer Trac as part of their package deals.Trac provides a number of interesting features that make it a tool particularly well suited to small teams using lightweight, agile development processes. These include:
- A lightweight issue-tracking system, where issues can be entered and assigned to team members with a minimum of formality and effort
- An excellent web interface to your Subversion source code repository, allowing you to browse your source code and revisions, and keep tabs, via both the web site and RSS feeds, on what changes are happening in the repository
- A wiki-based project management tool, in which you can manage project milestones and iterations, assign tasks, and share documentation and ideas
But one of the nicest things about Trac is the way all of these features are integrated together. Using a wiki-style syntax, you can include references to Trac issues, tasks, or wiki pages in your Subversion commit messages, or refer to Subversion changesets from within Trac. These references are rendered as HTML links on the project web site. And a project timeline gives you an overall view of all activity on your project, making it easy to keep track of changes made to issues, tasks, and within the source code .In this chapter, we will discuss how to install and use Trac in your projects.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing Trac
- InhaltsvorschauInstalling Trac is not a difficult task in itself, but the various dependencies can make things a little complicated, especially on a Windows machine. In addition, the Trac installation process tends to vary a great deal in its finer details from one system to another. In this chapter, we will cover the general steps and also a few tips to get you started. For more details, the best and most up-to-date reference remains the Trac web site itself (see ).One thing that you should know from the start is that, at the time of this writing, Trac did not support network access to a Subversion repository. So, you need to either install Trac on the same machine as your Subversion repository, or mirror the Subversion repository to your Trac server.Trac is written in Python, so you will need to have Python installed on your machine. Python is bundled with many Linux distributions these days, and Windows installers and installation packages for other OSs are readily available on the Python web site. However, if you want to use the mod_python module for Trac (see ), you may need to recompile Python yourself from the source .You will also need to install the Subversion Python Bindings. To do this on Windows, you can download and run the installer from the Subversion site, using version numbers corresponding to your local installation (e.g., svn-win32-1.4.5_py2.5.exe for Subversion 1.4.5 and Python 2.5). On a Linux box, this may well involve (re)building Subversion from the source code yourself with the correct configuration options. On Linux, another common error is to forget to update the library paths to include the newly compiled libraries (this usually involves defining or updating the LD__PATH variable).Trac also needs a relational database: you can use either SQLite, PostgreSQL, or MySQL, all of which work well on both Windows and Linux machines.By default, Trac will use an embedded SQLite database written in C, which is easy to configure and quite sufficient for most projects. If you want to use this option, however, you still need to install SQLite onto your machine. There are precompiled packages (RPM or DEB) available for many Unix distributions, and this is generally the easiest option. Alternatively, you can build it from the source code, as shown here (you will need the TCL development libraries for this to work):Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Setting Up a Trac Project
- InhaltsvorschauIn Trac, all project administration tasks are done at the command line using the trac-admin tool. Each Trac project has its own directory. To set up a new project, we use the initenv command, as shown here:
$ mkdir /data/trac $ trac-admin /data/trac/myproject initenv Creating a new Trac environment at /data/trac/myproject Trac will first ask a few questions about your environment in order to initalize and prepare the project database. Please enter the name of your project. This name will be used in page titles and descriptions. Project Name [My Project]> ...
Note that the trac-admin utility will create the project directory (myproject), but not the parent directories (/data/trac), which you must have created beforehand.On a Windows machine, you need to invoke python explicitly, as shown here:C:\Python23\Scripts>mkdir D:\trac C:\Python23\Scripts>python trac-admin D:\trac\myproject initenv
In both cases, this will run an interactive script prompting you for the configuration details Trac needs to set up your project, such as:- The name of the project
- This will be displayed at prominent places on your Trac web site, so make it meaningful.
- The database connection
- By default, Trac will use an embedded SQLite database, which is sufficient for small projects. You can also configure Trac to use a PostgreSQL database. If you want to use PostgreSQL, create a database called “trac,” and use a database connection string of the following form:
postgres://<user>:<password>@localhost/trac
- The Subversion repository
- Trac needs the local path of your Subversion repository (not a Subversion-type URL), and also needs write-access to set up its own access.You can run Trac without a Subversion repository, in which it can work as an issue-tracking tool and project wiki, but without the integration with the source control management tool. To do this, simply do not provide a repository path
Once the script has finished asking questions, it will proceed to build the Trac database in the directory you provided. If all goes well, you should get something like this:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Running Trac on the Standalone Server
- InhaltsvorschauTrac comes with a small, very fast embedded web server called tracd. This configuration is easy to install and configure and can be quite sufficient for many enterprise projects.To start the tracd server, run the tracd command as shown here:
$ tracd --port 8000 /data/trac/myprojectYour project will be available on http://localhost:8000/myproject.For a Windows installation, the instruction is slightly different (because of the way Python works under Windows), and you need to explicitly invoke the python executable, as shown here:C:\Python23\Scripts>python tracd --port 8000 D:\trac\myprojectIf you have several trac environments on the same server, just list them as follows:$ tracd --port 8000 /data/trac/project1 /data/trac/project2Your projects will be available on separate URLs: http://localhost:8000/myproject1 and http://localhost:8000/myproject2.However, this alone will not provide a very satisfying solution. Using this configuration, users will not be able to log on to the site. We need to set up user authentication.Subversion can be configured to work with an Apache server, and to use an Apache-style basic or digest authentication file (see ). If your Subversion server is set up to use such a file, you can tell Trac to use it, too. This has the obvious advantage of sharing user accounts directly between Subversion and Trac.To do this, you use the --basic-auth or --auth options when starting tracd from the command line. You need to specify an auth command-line option for each of your Trac projects. The auth option takes the following form:--auth project-name,/path/to/svn-digest-auth-file, realm-name
The realm name is the name specified in the AuthName parameter in your Apache configuration file (see ). Here is a complete example:python tracd -p 8000 \ --auth project1,/data/svn/svn-digest-auth-file,"Subversion repository" \ --auth project2,/data/svn/svn-digest-auth-file,"Subversion repository" \ /data/trac/project1 /data/trac/project2
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Tracd As a Windows Service
- InhaltsvorschauIt is quite easy to set up tracd as a Windows service using the techniques described in . Use InstSrv and Srvany to install a new service:
D:\trac>InstSrv tracd "C:\Program Files\Windows Resource Kits\Tools\srvany.exe" The service was successfuly added! Make sure that you go into the Control Panel and use the Services applet to change the Account Name and Password that this newly installed service will use for its Security Context.Next, you need to update the registry as shown for the Subversion service (see ). Open the Windows Registry and open the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\tracd entry (see ). Add a key called Parameters, and add two String values. The Application value should point to your python executable (typically something like “C:\Python23\python”). The AppParameters should contain the full path to the tracd script (for example,“C:\Python23\scripts\tracd”), followed by all the appropriate-line options (see ).Once this is done, the service should be up and running. You can test by starting and stopping the script from the command line as follows:
Figure : Configuring Trac as a Windows serviceD:\trac>net start tracd The tracd service is starting. The tracd service was started successfully. D:\trac>net stop tracd The tracd service was stopped successfully.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Installing Trac on an Apache Server
- InhaltsvorschauTrac is bundled with its own standalone server, which is fast and easy to install, but not as flexible as a full-blown web server such as Apache. You can also install Trac on an Apache server using either FastCGI or mod_python (CGI is also an option but not a recommended one).There are several ways to do this. Here, we will install Trac onto an Apache server using the mod_python module.This section refers to Apache 2.0 and mod_python 3.2.8. Both can be downloaded from the Apache web site. The key aspect here is to correctly install the mod_python module, although installation details may vary from version to version and between platforms. Check out the mod_python web site for details regarding how to install mod_python on your platform.In all cases, you will need to load the mod_python module in the Apache configuration file. This usually takes the form of a line like the following one in your
httpd.conffile:LoadModule python_module modules/mod_python.so
The Apache configuration is quite similar to the Subversion equivalent (see ). You can provide simple public access to your site for anonymous users by using the mod_python plug-in as follows:<Location /trac/myproject1> SetHandler mod_python PythonHandler trac.web.modpython_frontend PythonOption TracEnv "D:/trac/myproject1" PythonOption TracUriRoot "/trac/myproject1" </Location>
With this configuration, you could access your Trac site using a URL like http://localhost/trac/myproject1.You can set up authentication to use the same Apache basic or digest authentication that you use for your Subversion installation: simply use the same authentication type (AuthType), realm name (AuthName), and authentication file (AuthUserFile or AuthDigestFile). Just associate the authentication file with the “/login” URL for your project, as follows:<LocationMatch "/trac/myproject1/login"> AuthType Digest AuthName "Subversion repository" AuthDigestDomain /trac/myproject1 AuthDigestFile "/data/svn/svn-digest-auth-file" Require valid-user </LocationMatch>
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Administrating the Trac Site
- InhaltsvorschauBy default, normal Trac users can do everything they need to do in their daily development activity. However, to perform more advanced tasks, such as creating milestones or reports or adding new ticket types, you need to use an account with extra rights.In the standard installation of Trac, you manage user rights from the command line, using the trac-admin script. To list the current access rights, use the permission list command as follows:
$ trac-admin /data/trac/myproject permission list User Action -------------------------- anonymous BROWSER_VIEW anonymous CHANGESET_VIEW anonymous FILE_VIEW anonymous LOG_VIEW anonymous MILESTONE_VIEW anonymous REPORT_SQL_VIEW anonymous REPORT_VIEW anonymous ROADMAP_VIEW anonymous SEARCH_VIEW anonymous TICKET_CREATE anonymous TICKET_MODIFY anonymous TICKET_VIEW anonymous TIMELINE_VIEW anonymous WIKI_CREATE anonymous WIKI_MODIFY anonymous WIKI_VIEW Available actions: BROWSER_VIEW, CHANGESET_VIEW, CONFIG_VIEW, FILE_VIEW, LOG_VIEW, MILESTONE_ADMIN, MILESTONE_CREATE, MILESTONE_DELETE, MILESTONE_MODIFY, MILESTONE_VIEW, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE, REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW, SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, WIKI_VIEWIt is always useful to have an administrator account. Indeed, you need to create one to be able to do any serious customization work.$ trac-admin /data/trac/myproject permission add mike TRAC_ADMINMike is now administrator, and he can make any modifications that he wants to the site.Trac also allows more fine-grained permissions, both for users and for user groups. For example, the following command lets Jill create new reports, a function that is not available for normal users:$ trac-admin /data/trac/myproject permission add jill REPORT_CREATEAnother useful administration tool is the web administration console (see ). This module is integrated out-of-the-box into Trac 0.11, but for previous versions, you will need to download and install it yourself.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing User Accounts
- InhaltsvorschauIn the default Trac installation, managing user accounts is a little cumbersome. Although the web administration console (see ) does make it easier to manage user rights from the web interface, there is no way to create or delete users directly from the Web. You need to directly manipulate the Apache htpasswd or htdigest file from the command line.However, there is a better way. Matthew Good has written a third-party plug-in for Trac called AccountManager, which makes managing user accounts a great deal simpler. This plug-in provides several nice features in this domain, such as:
- Viewing, creating, and deleting users stored in the Apache passwd or digest file
- Letting users register to create their own accounts
- Using HTML form-based login rather than HTTP authentication
- Letting existing users change their passwords or even delete their accounts
- Sending an email to users who have forgotten their passwords
In this section, we will go through how to install and use this plug-in. First of all, you need to install the plug-in. Before starting, you should install the web administration plug-in (see ) if this is not already done. The exact instructions can be found on the plug-in web site. Installation is straightforward, and it uses the easy_install python script. For Trac 0.10, you do something like this:$ easy_install http://trac-hacks.org/svn/accountmanagerplug-in/0.10Once you have installed the plug-in, go to the “Manage plug-ins” section on the Admin page. There should be a new entry for the AccountManager plug-in (see ), where you can activate the various modules that come with the plug-in.The most common authentification setup in Trac is to use the same user logins and passwords that are used for the Subversion repository. If you are using Apache for your Subversion and Trac authentication, you will have either an HTDigest file or an HTPasswd file containing your users and encrypted passwords. You just need to tell the AccountManger plug-in which type of authentication you are using, and which file is being used, which you do in the Configuration screen (see ). The file and realm that you specify here need to correspond to the ones defined in your Apache configuration file for your normal Subversion authentication (see ).
Figure : Configuring the AccountManager plug-inEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tailoring the Trac Web Site: Using the Wiki Function
- InhaltsvorschauTrac is, first and foremost, a wiki-based application. Any page can be modified at will by any team member with the appropriate rights, and new pages can be created easily to describe specific aspects of your particular project.Trac’s online help describes the Trac wiki syntax in some detail. Most of the formatting language is very close to what you find on other wiki sites, and, in particular, that of the MoinMoin wiki engine. If you’ve never used a wiki before, here are some of the more useful markup codes:
- Headings
- Headings and subheadings can be written using “=” and “==,” as shown here:
= A heading = ... == A sub-heading == ...
- Lists
- You can use “*” for unordered lists, as shown here:
* Cats * Burmese * Siamese * Dogs * Bloodhound * Poodle
Trac expects you to respect some fairly strict formatting rules for lists: the first element must be indented by precisely one space and subsequent levels by exactly two spaces per level. - Formatting
- Basic text formatting such as bold and italic can be done using single quotes, as shown here:
* '''bold''' * ''italic''
Preformatted text can be displayed using {{{...}}}:{{{ public void foo(int bar) { ... } }}}
For example, consider the following text:= Feature: A clickable map = == Description == We need to display a map on the home page. Users can click to display the list of libraries in a region or city. == Non-functional requirements == Here are the ''non-functional'' requirements: * The user interface must be '''fast''' * Zooming should be really fast and easy * No browser refreshing during map updates * The colours must be pretty The server-side class must implement the following method: {{{ /** * The foo-bar function. */ public void foo(int bar); }}}This text is rendered in .
Figure : Formatted wiki textHowever, one of the most useful (and original) aspects of the Trac wiki is its ability to integrate with other Trac objects. Tickets, changesets, milestones, other wiki pages, and more can all be references using the special TracLink syntax. These hyperlinks can also be used both in commit messages and in object descriptions. Typical uses include listing corrected bugs when committing source code to the repository, referring to source code when describing work done to fix a bug, referring to a target milestone in a feature description, and so on. See for an example of a Trac ticket using the wiki formatting. Detailed documentation about available TracLink types is available in the Trac online documentation. Some of the more useful types are described here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using the Trac Ticket Management System
- InhaltsvorschauTickets are the bread-and-butter of the Trac issue management system. Tickets can be used to keep track of bugs and change requests, features, tasks, and just about anything else. The Trac ticket database is simple, flexible, and easy to customize. Trac is designed to be as lightweight and unobtrusive as possible. There are few constraints and virtually no mandatory fields, so users can put as little or as much information as they wish. (Of course, this may be a good thing or a bad thing depending on your particular .)As a project management tool, Trac is more suited to the more lightweight, agile methodologies. It makes no claim to be a web equivalent of full-blown project management tools such as Microsoft Project. There is no support for defining dependencies between tasks or for producing Gantt chart reports, for example, although support for dependencies between tasks (defining tasks and subtasks) is planned for release 1.0 of the product. Of course, you can always use the wiki links (see ) to list any dependencies in the ticket description. Another useful planning feature is to associate tasks, features, or bugs with milestones or versions (see ).In the rest of this section, we will look at how the Trac tickets can be used to improve your software development project.Creating a new ticket in Trac is straightforward. You can create Trac tickets quickly and easily in the “New Ticket” screen (see ). Although there are no mandatory fields, you should enter at least the summary field (this is what is displayed in the reports), a type, and a description. You can use the usual wiki formatting in the description field, so a devoted developer can refer to the source code where the problem was fixed and the changeset in which this correction was delivered, for example.
Figure : Creating a new Trac ticketThe Ticket Type field lets you specify what sort of issue it is: a bug, an enhancement, a task, and so on. Ticket types are fairly arbitrary, and can be used to extend the ticket management system to far beyond simple issue tracking. Modifying the type of a ticket has no effect on any of the other fields: its main use is to identify different types of tickets and as a filter in reports and queries.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Updating Trac Issues from Subversion
- InhaltsvorschauOne nice trick is to get Subversion to update the relevant Trac tickets whenever someone commits changes to the repository. By using a postcommit hook (see ), you can configure Subversion to update any Trac tickets referenced in the commit message. A sample script can be found on the Trac web . Just download this script and store it on your server in a convenient place (say,
/usr/share/trac/contrib/trac-post-commit-hook, or directly in the Subversionhooksdirectory). Make sure that the script matches your version of Trac, as the scripts don’t tend to be compatible between versions.Subversion hook scripts are stored in thehooksdirectory of your Subversion repository. For example, if your repository is stored in the/var/svn/reposdirectory, the hooks directory will be at/var/svn/repos/hooks:# cd /var/svn/repos/hooks # ls post-commit post-revprop-change.tmpl pre-lock.tmpl start-commit.tmpl post-commit.tmpl post-unlock.tmpl pre-revprop-change.tmpl post-lock.tmpl pre-commit.tmpl pre-unlock.tmpl
This directory contains mostly templates showing how to write Subversion hooks. The real hook scripts are the ones without the “.tmpl” suffix. In our case, we need a script calledpost-commit, which will call the script we just downloaded. You will find more detailed instructions in thetrac-post-commit-hookscript, but a typical implementation looks something like this (for Trac 0.10.4):#!/bin/sh # POST-COMMIT HOOK REPOS="$1" REV="$2" LOG=`/usr/bin/svnlook log -r $REV $REPOS` AUTHOR=`/usr/bin/svnlook author -r $REV $REPOS` TRAC_ENV='/var/trac/myproject/' TRAC_URL='http://buildserver/trac/myproject/' /usr/local/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \ -p "$TRAC_ENV" \ -r "$REV" \ -u "$AUTHOR" \ -m "$LOG" \ -s "$TRAC_URL"
Make sure both these scripts are executable, or Subversion will not be able to trigger them correctly. Subversion hooks are run with no environment variables initialized, so you may also need to define environment variables such asEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing Trac Ticket Fields
- InhaltsvorschauTrac lets you customize the ticket screen to some extent. All of the predefined lists can be customized in the Admin screens (see ). If you don’t need a field, remove all its values and it won’t be displayed on the “Ticket” screen.
Figure : Customizing ticket fields in the Admin screensBy default, the “Assigned to” field is a text field. In many cases, it is more convenient to display a list of known Trac users. To do this, open the Trac configuration file, which you can find in <projectenv>/conf/trac.ini, and set the restrict_owner variable in the ticket group to true:[ticket] restrict_owner = true
This is actually only part of the picture. You also need user names to fill this list. At the time of this writing, Trac has no centralized interface where user accounts are managed. Users have to set up their own user accounts and email details in the “Settings” screens (see ). Users who do this will automatically appear in the user list.You can also add additional fields to the “Ticket” screens, although not via the web interface. If you want to add extra fields, you need to configure them in the Trac configuration file. You configure custom ticket fields in the [ticket-custom] block. Suppose that you want to add a simple text field for developers to record the time spent fixing a bug. This could be done as follows:[ticket-custom] correctiontime = text correctiontime.label = Time taken to correct
Now suppose that you need a list of operating systems:[ticket-custom] correctiontime = text correctiontime.label = Time taken to correct os = select os.label = Operating System os.options = All|Windows|Mac|Linux|Other os.value = 1
Custom tickets can be of any of the HTML input types: text, checkbox, select, radio, or textarea.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up Email Notifications
- InhaltsvorschauTo be efficient, users should be informed of any new or modified tickets as soon as possible. Trac notifies users by email. You can set this up in the Trac configuration file (<projectenv>/conf/trac.ini), in the [notifications] section. By default, email notification is deactivated. To activate it, set smtp_enabled to true, and provide sensible values for the other fields (which are mostly self-evident). The always_notify_owner, always_notify_reporter, and always_notify_updater fields let you specify who should be systematically informed of any changes made to a ticket:
[notification] smtp_enabled = true smtp_server = mail.mycompany.com smtp_from = trac@mycompany.com smtp_replyto = myproj@mycompany.com smtp_always_cc = projectmanager@mycompany.com, qa@mycompany.com always_notify_owner = true always_notify_reported = true always_notify_updater = true
This leaves just one minor detail: to send notifications to your users, Trac needs to know their email addresses. One approach is to use email addresses as usernames. This works, but it’s not particularly elegant. In fact, there is an easier way.The Settings page (see ) lets you associate an email address with your username. That way, you can have nice short usernames and still get your email .
Figure : The Settings pageEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Reporting Using Trac Queries and Reports
- InhaltsvorschauFor a long time, Trac users built reporting functions by using direct SQL queries on the Trac database. You can see an example in . Although this approach is still in use, and currently offers the richest feature set, it is progressively being phased out in favor of a more intuitive query language, which can be configured via the web and also used directly within wiki pages (see ).You configure Trac reports and queries in the “View Tickets” screen. Trac comes with a number of useful predefined reports such as “Active Tickets,” “My Tickets,” and “All Tickets by Milestone.” If you need to define your own, you have to write your own custom SQL query. The following query, for example, lists all new, assigned, or reopened tickets for milestone1:
Figure : A Trac reportSELECT id AS ticket, status, severity, priority, owner, time as created, summary FROM ticket WHERE status IN ('new', 'assigned', 'reopened') and milestone = 'milestone1' ORDER BY priority, timeThis rather low-level approach is destined to be replaced by a more user-friendly one in which you dynamically build your query using a series of filters and group-by criteria (see ).
Figure : A Trac reportThis promising feature is not yet fully functional. At the time of this writing, you can’t actually store queries, and not all of the features available in the SQL reports have been implemented. However, you can insert them into any wiki page using the TicketQuery macro (see ). To do this in the current version, you need to write your query using the special Trac query .Trac uses a simple query language in “query” links and in TicketQuery macro expressions, both of which can be placed directly in any wiki page. This language is simply a set of filter expressions separated by ampersands (&). Alternative values are separated by the pipe (|) character. For example, the following expression displays the list of all open (new, assigned, or reopened) tickets:[query:status=new|assigned|reopened]
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Managing Progress with Trac Roadmaps and Timelines
- InhaltsvorschauTrac provides convenient ways to plan, monitor, and publish information about project progress. In this section, we will look at how to plan, track and publish project progress using the “Milestone” screen, and how to keep tabs on detailed project activity using the “Timeline” screen.The “Roadmap” screen (see ) gives an overview of current and future project milestones, including a description of each milestone and an indication of the progress made (based on the percentage of closed tickets associated with each milestone). Milestone descriptions use the Trac wiki formatting (see ), so they can contain references to other Trac objects such as tickets or changesets.To get the most out of this functionality, you need to integrate Trac into your daily project management. As part of the project planning activity, you create development tasks and associate them with different milestones. If an iterative approach is used, milestones may correspond to iterations, and tasks may be used to keep track of features or user stories.
Figure : The Trac Roadmap viewFrom a project management point of view, the “Roadmap” view of project progress has a lot going for it:- It gives a high-level overview of project progress useful for keeping management people happy.
- It requires very little extra overhead in terms of reporting or project management: progress is measured on the basis of the proportion of open tickets for a given milestone.
- The metrics used to measure progress are objective and Boolean: a ticket is closed or open, it is never 90 percent done.
- It can accurately reflect situations in which work may be progressing in parallel in several areas, toward different milestones.
If you need to know more about progress on a particular milestone, you can always click on the milestone to open a more detailed view (see ). In addition to the progress indicators visible in the “Roadmap” view, the “Milestone” view shows a graph of ticket status grouped by type, owner, component, severity, priority, and so on. Clicking on any of the graph lines will drill down further to the detailed list of all the corresponding tickets.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Browsing the Source Code Repository
- InhaltsvorschauTrac provides full web access to the underlying Subversion repository (see ).All directories and files are listed with their latest revision number. You can click on any revision number to display the corresponding change log. From here, you can view the changes between two revisions or the changes that were done in a particular file (see ).
Figure : Browsing the source code repository
Figure : Viewing changes in the source codeYou can also browse a previous version of the source code, using the “View Revision” field.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using RSS and ICalendar
- InhaltsvorschauFor users who don’t consult the Trac web site several times a day, or who simply wish to be able to keep track of project progress without having to open a new web page, Trac provides RSS support for several types of project data. The “Timelines” screen provides an RSS feed concerning all project events, whereas each individual Report and Query can provide an RSS field listing the current tickets matching certain criteria.Subscribing to an RSS feed can be done in a number of ways. Pages with an RSS feed available have the usual orange “XML RSS feed” icon at the bottom of the screen, which you can usually click on to subscribe. Not all browsers support this function, however. Recent versions of Firefox also provide support for Live Bookmarks; when you display a page with RSS support, an orange icon appears in the URL. You can click on this icon to subscribe (see ).
Figure : Subscribing to an RSS feedOnce you’ve subscribed to the RSS feed, you can now keep tabs on the latest project activity or the status of tickets, just as you would any other RSS feed. In , for example, you can see ticket status being displayed in Firefox using the Live feature. There are, of course, many other tools and techniques to display RSS feeds: any tool that suits your work habits will do the job.
Figure : Monitoring a Trac report via an RSS feedTrac also lets you download tasks from the Roadmap page in the standard iCalendar format. iCalendar is a standard data-exchange format designed to exchange meetings and tasks. It is supported by a number of products, including Lotus Notes, KOrganiser, Evolution, and Microsoft Outlook. From the RoadMap page, you can download data in iCalendar format and import the corresponding tasks into your calendar application. However, if your calendar tool lets you, it is more convenient to subscribe to the Trac site as a remote iCalendar calendar. This way tasks that are assigned to you are automatically updated in your local calendar.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing a Wiki Page with Python
- InhaltsvorschauThe Trac pages are extensible and easy to configure. In addition to modifying the text and layout of your wiki pages, you can also insert dynamic contents using macros.Macros are enclosed in two square brackets and can take parameters provided between parentheses. Trac comes with a dozen or so standard macros, letting you do a few simple tasks. We’ll look at some of the more interesting ones.The Image macro inserts the image of your choice into the page. Images can come from a variety of places, including files attached directly to the current wiki page, as shown here:
[[Image(screenshot.gif)]]
Files can also come from other wiki pages or objects, or even from the repository, as shown here, from a ticket:[[Image(ticket:16:screenshot.jpg)]]
and from the repository:[[Image(source:/dev/JavaLamp/trunk/src/main/resources/images/green-lavalamp.gif)]]
The RecentChanges macro lists pages which have been recently modified. This can be handy on the home page, for example, in a “What’s new” zone. You can specify the prefix of the pages you want to display and/or the maximum number of results. For example, the following macro lists the five most recently modified pages whose names begin with “Wiki”:[[RecentChanges(Wiki,5)]]
Finally, the TicketQuery macro lets you display the result of queries on the ticket base (see ):== Curren Open Tickets == [[TicketQuery(status=assigned)]]
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauTrac is, as the writers maintain, a lightweight issue tracking system with excellent Subversion integration. On the downside, it is not as feature-rich as many other issue-tracking solutions, and its reporting capabilities in the current version are somewhat limited. Nevertheless, for small, agile teams using Subversion, Trac is an excellent choice.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 29: Team Communication with the Project Web Site
- InhaltsvorschauContributed by: Eric RedmondTeam communication is an essential part of any project. Wasting time looking for technical project information can be costly and frustrating. Clearly, any IT project will benefit from having its own dedicated technical web site. That’s where the Maven2 site generator steps in. With little effort, you can have a professional-quality, low- project web site up and running in no time. Maven lets you generate a one-stop center of information about your project, including:
- General project information such as source repositories, defect tracking, and team members
- Unit test and test coverage reports
- Automatic code reviews with Checkstyle and PMD
- Configuration and versioning information
- Dependencies
- Javadocs
- Source code in indexed and cross-referenced HTML format
- And much more
Maven sites are frequently used on open source projects. A typical project site contains information about the project, largely derived from the pom.xml file, some generated reports (unit tests, Javadocs, Checkstyle code audits, etc.), as well as some project-specific content. In this chapter, we will look at how to generate different parts of the Maven web site.Your Maven site will be one of the first places that newcomers will look to get to know your project, and the project information page is the place they will expect to find a summary of your project’s organization. Much of this web site is built using information found in the pom.xml file. In this section, we will go through the basics of building an informative Maven web site.The standard way to create a Maven site is to use the mvn site command, as shown here:$ mvn site [INFO] Scanning for projects... [INFO] ---------------------------------------------------------------------------- [INFO] Building Spitfire [INFO] task-segment: [site] [INFO] ---------------------------------------------------------------------------- ... [INFO] Generate "Dependencies" report. [INFO] Generate "Issue Tracking" report. [INFO] Generate "Project License" report. [INFO] Generate "Mailing Lists" report. [INFO] Generate "About" report. [INFO] Generate "Project Summary" report. [INFO] Generate "Source Repository" report. [INFO] Generate "Project Team" report. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5 seconds [INFO] Finished at: Thu Jan 04 09:55:59 NZDT 2007 [INFO] Final Memory: 11M/22M [INFO] ------------------------------------------------------------------------Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Maven 2 Project Web Site As a Communication Tool
- InhaltsvorschauContributed by: Eric RedmondTeam communication is an essential part of any project. Wasting time looking for technical project information can be costly and frustrating. Clearly, any IT project will benefit from having its own dedicated technical web site. That’s where the Maven2 site generator steps in. With little effort, you can have a professional-quality, low- project web site up and running in no time. Maven lets you generate a one-stop center of information about your project, including:
- General project information such as source repositories, defect tracking, and team members
- Unit test and test coverage reports
- Automatic code reviews with Checkstyle and PMD
- Configuration and versioning information
- Dependencies
- Javadocs
- Source code in indexed and cross-referenced HTML format
- And much more
Maven sites are frequently used on open source projects. A typical project site contains information about the project, largely derived from the pom.xml file, some generated reports (unit tests, Javadocs, Checkstyle code audits, etc.), as well as some project-specific content. In this chapter, we will look at how to generate different parts of the Maven web site.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Setting Up a Maven Site Project
- InhaltsvorschauYour Maven site will be one of the first places that newcomers will look to get to know your project, and the project information page is the place they will expect to find a summary of your project’s organization. Much of this web site is built using information found in the pom.xml file. In this section, we will go through the basics of building an informative Maven web site.The standard way to create a Maven site is to use the mvn site command, as shown here:
$ mvn site [INFO] Scanning for projects... [INFO] ---------------------------------------------------------------------------- [INFO] Building Spitfire [INFO] task-segment: [site] [INFO] ---------------------------------------------------------------------------- ... [INFO] Generate "Dependencies" report. [INFO] Generate "Issue Tracking" report. [INFO] Generate "Project License" report. [INFO] Generate "Mailing Lists" report. [INFO] Generate "About" report. [INFO] Generate "Project Summary" report. [INFO] Generate "Source Repository" report. [INFO] Generate "Project Team" report. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5 seconds [INFO] Finished at: Thu Jan 04 09:55:59 NZDT 2007 [INFO] Final Memory: 11M/22M [INFO] ------------------------------------------------------------------------This will create a simple Maven site, shown in .A site like this will be pretty sparse. You will probably want to add some details about your project. All zones are optional, but it is a good idea to put as much information as is relevant to your project. The rest of this section will discuss some useful items to add.The Maven site is above all a communication tool, so it is important to let people know what your project is all about. The POM file lets you provide plenty of details about your project, such as an appropriate project name, a short description, the project home page, and your organization’s details. Here is a typical example:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Integrating Reports into Your Site
- InhaltsvorschauMaven also provides an extensive range of plug-ins for automatic report generation. Reports are a crucial part of the Maven web site, and can provide useful information on the state of the project from a technical point of view, as well as providing reference documentation such as Javadocs. Report generation in Maven 2 is easy: you just add the report plug-ins you need into the reporting section at the end of the pom.xml file.Most likely, you will want to start by publishing your classes’ Javadocs. This is as simple as adding Javadoc to the reporting section of your pom.xml file. While you’re at it, add the jxr plug-in; this will generate an indexed and cross-referenced HTML version of your source code:
<project> <build> ... </build> <reporting> <plugins> <plugin> <artifactId>maven-javadoc-plugin</artifactId> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jxr-maven-plugin</artifactId> </plugin> </plugins> </reporting> <dependencies> ... </dependencies> </project>Writing unit tests for as much code as possible is highly recommended. Maven fully integrates unit testing into the build process—by default, all unit tests run at each build. Publishing test results for all to see is beneficial, since it tends to encourage developers to fix any broken unit tests. The Surefire plug-in runs all the unit tests and generates a detailed report:<reporting> <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> </plugin> </plugins> </reporting>Test coverage can be a useful indication of the quality of your unit tests. Basically, it tells you how much of your code is actually run by your unit tests, which, in turn, can give you a good idea of the tests’ quality. In some projects, requiring a certain percentage of test coverage in all classes is recommended. Tools such as Clover (a vigorous commercial test coverage tool) or Cobertura (see ) generate useful test coverage reports. We discussed how to integrate Cobertura reports into Maven in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Creating a Dedicated Maven Site Project
- InhaltsvorschauContributed by: Eric RedmondAlthough any individual project can have its own site, it is often easier to manage a separate site project. In the following sections, we will look at how to build and customize a separate site project. However, all of this information can just as easily apply to a project that contains code as well.The simplest way to bootstrap any Maven site development is to use the maven-archetype-site archetype, as shown here:
$ mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-site \ -DarchetypeVersion=1.0 \ -DgroupId=com.mycompany \ -DartifactId=my-siteThis will generate a sample Maven site project, which may be built by executing thesitebuild lifecycle. The structure of your new Maven site project should look like this:my-site |-- pom.xml `-- src `-- site |-- apt | |-- format.apt | `-- index.apt |-- fml | `-- faq.fml |-- fr | |-- apt | | |-- format.apt | | `-- index.apt | |-- fml | | `-- faq.fml | `-- xdoc | `-- xdoc.xml |-- xdoc | `-- xdoc.xml |-- site.xml `-- site_fr.xmlThe items to note in the above structure are thesite.xmlfile and the directories undersrc/site:apt,fmlandxdoc; each representing a document notation type. You should have an index file somewhere (for example, a file called index.apt in thesite/apt). This will be converted to a file calledindex.html, which will become the home page of your site. Without this file, Maven will generate a very boring home page.Finally, thesite_fr.xmlandsrc/site/frsubdirectories contain a French language version of the documents, rounding out Maven’s commitment to built-in internationalization. To activate this locale, you simply add the French language version to the site plug-in’s configuration in the POM, as shown here:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Defining the Site Outline
- InhaltsvorschauContributed by: Eric RedmondThe
site.xmlfile is responsible for the structure of the site as a whole, defining the site’s banners, the links (upper-right corner by default), and the left-hand side menu. A simple example is shown here:<project name="Spitfire"> <bannerLeft> <name>Spitfire</name> <src>/images/spitfire-logo.jpg</src> <href>http://spitfire.mycompany.com/</href> </bannerLeft> <bannerRight> <src>/images/hurricane-small.jpg</src> </bannerRight> <body> <links> <item name="Apache" href="http://www.apache.org/" /> <item name="Maven 2" href="http://maven.apache.org/maven2/"/> <item name="Hibernate" href="http://www.hibernate.org/"/> </links> <menu name="The Spitfire project"> <item name="Home" href="index.html"/> <item name="APT Format" href="format.html"/> <item name="FAQ" href="faq.html"/> <item name="Xdoc Example" href="xdoc.html"/> </menu> ${reports} </body> </project>Note that links in the menu defined below have similar names to the documents above, yet end with.html. This is because each of the document markups are converted to HTML by the site plug-in, more specifically, by Doxia.A common initial requirement is to incorporate project and/or company logos. This is easy to do. The <src> tag in each banner points to an image to be displayed there. You can either use an absolute URL, or refer to a local resource within your web site project. To do the latter, just place any images you need to access in the site/resources directory. From here, you can access them using a local reference such as the “/images/spitfire-logo.jpg” shown above.You may or may not want to include the standard Maven project reports in your custom site. If you do, you need to include the ${reports} tag shown in the site.xml file above. This will be replaced by the “Project Information” and “Project Reports” menus that you have configured in your pom.xml file.The resulting web site is illustrated in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Maven Site Generation Architecture
- InhaltsvorschauContributed by: Eric RedmondYou can use a number of different formats to build the content of your Maven 2 web site. This is largely a matter of taste. We will examine the basic architecture of the Maven 2 content generation system and run through the formats that you can use.The heart of the Maven site plug-in is the Doxia project, which is a self-contained subproject of Maven (http://maven.apache.org/doxia). Doxia is responsible for converting your chosen document markup to the html that can be deployed to a web site. Doxia also has the ability to convert a document from any supported markup to any other supported markup. It does this with parsers and sinks. A Doxia parser implements the
org.apache.maven.doxia.parser.ParserPlexus role and is responsible for converting the implemented markup into a given sink. A Doxia sink implements theorg.apache.maven.doxia.sink.SinkPlexus role and caters for a particular output format, for example XHTML or PDF. This is important to know if you ever decide to write your own modules for Doxia to support.Almost Plain Text, or APT, can be considered the default documentation method for Maven projects. It is a simple wiki-like syntax that is easily converted to a Doxia sink. If you built the site generated by maven-archetype-site, navigate totarget/site/format.htmland you will be treated to a comprehensive guide for using APT. A simple example is shown here:----- The Hotel Database Vision Statement ----- The Wakaleo Team ----- January 2006 Introduction One of the nicer features of Maven is the ability to create an internal technical web site at very little cost. Maven 2 extends this functionality, and gives you powerful new ways to generate site content... * Sub-section title Team communication is an essential part of any project. And wasting time looking for technical project information can be costly and frustrating. Clearly, any IT project will benefit from having its own dedicated technical web site... * Item 1 * Item 2 * Item 2.1 * Item 2.2Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Using Snippets
- InhaltsvorschauContributed by: Eric RedmondA useful feature of Doxia are snippets. Snippets are macros that are given a file and an id. They can be a useful way of inserting or referencing key blocks of code from within your project in your technical documentation. You define a snippet as follows:
%{snippet|id=myID|file=some.file}You define a snippet by encapsulating a block of code in specially formatted comments. The macro will search the file for the commentSTART SNIPPET: myIDand ending commentENDSNIPPET:myID. For example, add the following to your POM:<project> ... <!-- START SNIPPET: props --> <properties> <my.prop>value</my.prop> </properties> <!-- END SNIPPET: props --> </project>Then add the following to yourformat.aptfile:%{snippet|id=props|url=file:pom.xml}Now run thesitephase, and be amazed! The block of code will now figure in the generated web page (see ).Hint: rather than running the
Figure : An example of a snippetsitephase, try running thesite:rungoal. It will launch your documents under a web container accessible from port 8080 that will show your changes in real time.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Customizing the Look and Feel of Your Site
- InhaltsvorschauContributed by: Eric RedmondWhether it’s just the company logo in the corner of the screen, or the whole site repainted using your company fonts and colors, adding that personal touch to your Maven site can do wonders for its appeal. You’d be surprised how many people pay more attention when the site matches the company colors! Maven provides a number of ways for you to customize the look and feel of your Maven web site. Here, we look at several ways to add your own personal touch.Like many sites, the look-and-feel of a Maven site is determined by what we call a “skin.” A skin is, in fact, a collection of CSS stylesheets and graphical resources. Changing skins in Maven is easy. Just alter your POM, and voilà! Your entire site’s look and feel has been altered. Better yet, it is all consistent. To switch the
my-siteproject to use the Stylus theme, alter thesite.xmlfile (not the POM):<project> ... <skin> <groupId>org.apache.maven.skins</groupId> <artifactId>maven-stylus-skin</artifactId> <version>1.0</version> </skin> </project>This style is illustrated in .The list of default skins available from the Maven Central Repository can be found at http://repo1.maven.org/maven2/org/apache/maven/skins/. At the time of this writing, the list is:- maven-classic-skin
- maven-default-skin
- maven-stylus-skin
As you probably guessed, maven-default-skin is the default.
Figure : The Stylus skinThe predefined Maven skins give you some flexibility, and they can help you learn how to customize your site’s look and feel. They are often just fine for internal sites with a limited, technically minded audience. However, there are times when the default skins just don’t cut it. Let’s see how to create your own personalized skin.First, create a project with the following layout:my-skin |-- pom.xml `-- src `-- main `-- resources |-- css | `-- maven-theme.css `-- imagesYou will also need a simpleEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Distributing Your Site
- InhaltsvorschauContributed by: Eric RedmondThe final step in site generation is publishing it to a location from which a public web server can access it. There is a special element in the
distributionManagementelement calledsite, which gives the location and method of distribution. In yourmy-sitePOM, you will notice the element has been added for you. Now you merely need to set it like any other Maven distribution. The example uses secure copy protocol (scp):<project> ... <distributionManagement> <site> <id>website</id> <url>scp://webhost.company.com/www/website</url> </site> </distributionManagement> </project>If you need to supply a username and password when you copy to the remote site, you will also need to provide this information in your settings.xml file (see ):<settings> ... <servers> <server> <id>website</id> <username>scott</username> <password>tiger</password> </server> </servers> </settings>Setting up an enterprise repository was covered in .With a suitable distribution setup, you merely run thesite-deployphase (which has bound to it thesite:site.deploygoal):mvn site-deploy
That’s it! Maven will generate the documentation using your chosen template and skin, and copy those files to the specified location.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 30: Automatically Generating Technical Documentation
- InhaltsvorschauGood-quality, reliable, and up-to-date technical documentation on a project is generally regarded as a Good Thing. Technical documentation is a great aid for the developers who are assigned to maintaining the product, once the original development team has been dispersed. Technical documentation can also help new developers come up to speed when they join a project.However, good technical documentation comes at a cost—the more documentation you have, the more effort you need to put into maintaining this documentation. And the effort that goes into maintaining technical documentation is, of course, effort that could be spent on writing code. In this chapter, we look at three tools that you can use to reduce the time you spend on maintaining your technical documentation, without compromising too much on the quality: SchemaSpy, for database models, and Doxygen and UmlGraph for source code documentation.Relational databases play a key role in a majority of today’s software applications, and database schemas are without doubt one of the most widely recognized and understood models in the IT field. Although they are relatively low-level, and lack some of the abstraction of an object-oriented class model, they can still be an extremely valuable asset when it comes to trying to understand the architecture of an application, both during the development phase and also during product maintenance once the application is in production.However, the main problem with database schemas, like all other design models and documents, is keeping them up-to-date. As a rule, the chances of a design document being kept up to date are inversely proportional to the effort required to maintain them. That is to say, if human intervention is required to maintain a document, it will soon become out of date in all but the most rigorous of projects.SchemaSpy can be a valuable asset in maintaining and publishing these models. Like Javadoc for Java source code, SchemaSpy provides an accurate, up-to-date view of the database structure, generated automatically from the actual database. As an automated tool, SchemaSpy incurs very little cost to set up and virtually no cost to maintain—the diagrams are automatically updated as the database schema evolves. This alone is a valuable time-saver for both development and maintenance teams.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Introduction
- InhaltsvorschauGood-quality, reliable, and up-to-date technical documentation on a project is generally regarded as a Good Thing. Technical documentation is a great aid for the developers who are assigned to maintaining the product, once the original development team has been dispersed. Technical documentation can also help new developers come up to speed when they join a project.However, good technical documentation comes at a cost—the more documentation you have, the more effort you need to put into maintaining this documentation. And the effort that goes into maintaining technical documentation is, of course, effort that could be spent on writing code. In this chapter, we look at three tools that you can use to reduce the time you spend on maintaining your technical documentation, without compromising too much on the quality: SchemaSpy, for database models, and Doxygen and UmlGraph for source code documentation.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Visualizing a Database Structure with SchemaSpy
- InhaltsvorschauRelational databases play a key role in a majority of today’s software applications, and database schemas are without doubt one of the most widely recognized and understood models in the IT field. Although they are relatively low-level, and lack some of the abstraction of an object-oriented class model, they can still be an extremely valuable asset when it comes to trying to understand the architecture of an application, both during the development phase and also during product maintenance once the application is in production.However, the main problem with database schemas, like all other design models and documents, is keeping them up-to-date. As a rule, the chances of a design document being kept up to date are inversely proportional to the effort required to maintain them. That is to say, if human intervention is required to maintain a document, it will soon become out of date in all but the most rigorous of projects.SchemaSpy can be a valuable asset in maintaining and publishing these models. Like Javadoc for Java source code, SchemaSpy provides an accurate, up-to-date view of the database structure, generated automatically from the actual database. As an automated tool, SchemaSpy incurs very little cost to set up and virtually no cost to maintain—the diagrams are automatically updated as the database schema evolves. This alone is a valuable time-saver for both development and maintenance teams.Based on material contributed by: Masoud KalaliSchemaSpy is a free, open source Java tool written by John Currier that can generate a rich interactive HTML view of your database, showing detailed information about the tables, views and relations that make up the database. SchemaSpy works by analyzing the metadata from a JDBC connection, and producing a set of detailed HTML reports describing the database. The HTML reports are richly annotated with hyperlinks letting you navigate between parents and children by simply clicking on the appropriate foreign key field. Primary keys, foreign keys, and indexes are visible at a glance.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Generating Source Code Documentation with Doxygen
- InhaltsvorschauDoxygen is a tool for generating technical documentation from your Java source code written by Dimitri van Heesch. Doxygen is similar to the standard Javadoc tool: both tools analyze Java source code to produce interactive HTML documentation. However, Doxygen goes further than Javadoc: as well as documenting the classes, it also can generate a variety of diagrams, such as class diagrams, collaboration diagrams, and dependency graphs. Doxygen is not limited to Java source code. It also can document C, C++, and C# projects. It also can produce technical documentation in other formats, such as PDF, RTF, or LaTeX.Another advantage of Doxygen is that it is much less obtrusive than Javadoc, making it easier to document your classes without having to think about formatting issues. For example, Doxygen will automatically recognize references to other classes (no need for @link commands), and uses a simple wiki-style notation for formatting, eliminating the need to add HTML code within your comments. This smooths the documentation process and makes for higher-quality, more relevent technical documentation.Doxygen generates a rich set of browsable HTML documentation (see ). By way of comparison, Doxygen-generated documentation is similar to but more extensive than documentation generated by Javadoc. In addition to the lists of classes, packages, and files that you would expect, Doxygen can generate UML class diagrams and diagrams which can provide a better understanding of how the classes work together. Collaboration diagrams are based on structural relations (inheritance and member variables), but they do not include class usage within individual methods.The reports also include annotated and cross-referenced source code in HTML form.
Figure : View of a class in the Doxygen reportDoxygen is not a Java API: it is a command-line utility that you need to download and install on your machine. There are precompiled binary installations for many Linux platforms such as Red Hat, SuSE, and the Debian family. Otherwise, you will need to download, compile, and install the tool. This fairly straightforward procedure is described here. First, download and unpack the Doxygen source code:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Embedding UML Diagrams in Your Javadoc with UmlGraph
- InhaltsvorschauSource-generated UML diagrams are an excellent way to communicate details of your application architecture and domain model. UML can provides a clear and (usually) readable representation of the state of your code. It can also highlight architectural flaws such as redundant dependencies or overly complex classes.You can use generated diagrams both to learn about an API and also to verify that an application has been correctly implemented. For example, it is much easier to review and explain a domain model implementation using interactive UML diagrams than by just reading the source code. Typically, a modeling tool might be used to design a clean domain model. Round-trip UML tools are not particularly widespread, however, and in most organisation, most application coding ends up being done directly in the IDE. Generating UML diagrams directly from source code can let you make sure that the implemented classes correspond to the target model.Diomidis D. Spinellis has written, and maintains, another interesting tool in this domain, called UmlGraph. This quite brilliant tool lets you insert detailed UML class diagrams seamlessly into your Javadoc. It does not go as far as Doxygen in the documentation that it generates—indeed, this tool is exclusively devoted to generating UML diagrams from your code. However, it does this very well.Like Doxygen, UmlGraph relies on the Graphvis tool, so this needs to be installed on your system before you can use UmlGraph.The most convenient way to use UmlGraph is via the UmlGraph doclet. This enables you to integrate UML class diagrams seamlessly in to your Javadoc (see ).These graphs are interactive, so you can click on a class to go to the Javadoc page (and corresponding UML class diagram) of that class. This provides a clear, intuitive way of navigating though the application architecture.
Figure : Javadoc embedded with UML class diagramsUmlGraph is a command-line tool, but, in a real-world project, this is not necessarily the most convenient way to generate your graphs. It is far easier to integrate UmlGraph into your build process. To this end, UmlGraph also comes in the much more useful form of a Javadoc doclet, making it easy to integrate into your javadoc generation. This approach works perfectly with Ant and Maven.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauGood technical documentation is a valuable asset for any project. Now, really good technical documentation can only be produced by a combination of human-written text and automatically generated documentation. Some high-profile open source projects such as Hibernate and the Spring frameworkcan be said to have high-quality documentation, combining well-crafted human-written documentation alongside automatically generated API documentation. It would be a mistake to think that the latter can replace the former.However, for typical enterprise projects, budgetary constraints may not allow for such high-quality documentation, nor is it absolutely essential for all types of projects to have this level of technical documentation. Tools such as SchemaSpy, Doxygen, and UmlGraph—although not producing truly great documentation—can certainly go a long way to producing adequate documentation. Combined with good-quality and strategically placed comments, they can still provide valuable insight into the architecture and inner workings of an application, at a relatively small cost.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
Zurück zu Java Power Tools
