A well-organized project directory structure makes both the code and the build process easier to manage. A number of factors, including the requirements of the target application server and the needs of the development tools used by the project team, drive the exact structure of your particular build process. However, the concepts presented here provide an outline that you can tailor for your specific project environment.
The project structure becomes more elaborate as the application grows in size. By keeping things simple from the start, you can help avoid undue complexity as the system evolves.
For J2EE development, the project structure falls logically into the four separate areas: source, build, external libraries, and distribution. Each area is normally contained within its own directory on the file system, with each directory sitting directly beneath a top-level project directory in the hierarchy.
Figure 12-3 depicts the top-level project structure.
Figure 12-3. Top-level directory structure.
We start by examining the structure of the source directory.
The source directory contains all program artifacts involved in the construction of the application. This is not limited to Java source but encompasses such artifacts as build scripts, property files, deployment descriptors, database scripts, and XML schemas. Figure 12-4 shows the organization of the source directory.
Figure 12-4. Source directory structure.
The project is structured around the creation of a single EAR file, with each module of the distribution being maintained as a subproject.
All source code should reside under its package directory structure. You should also consider adding a parallel source directory for each component to maintain the code for unit tests.
From the directory structure shown in Figure 12-4, we have several modules:
Each module that makes up the J2EE distribution possesses its own build.xml file. These are self-contained build files, capable of building each module in isolation. The build files are also aware of dependencies that may exist between the different modules. A Web application will likely call into a business layer and so may have a dependency on the EJB module. This should all be managed by each build file.
All build files should be consistent between modules, with each build file exposing the same set of targets. A single controlling build file resides at the top of the project directory structure. This is a delegating Ant build file, again exposing the same targets as the build files of each subproject.
Calls to targets in the project build file are cascaded down to the equivalent targets in each of the subprojects. Although each build file has the same set of targets, the work they perform is in context with the type of module they are responsible for building. On a Web module, the target package produces a WAR file, while on the top-level project build file, the package target generates an EAR file.
The build order of the subprojects is important and should commence with the low-level modules and work upwards. Building from bottom-to-top and ensuring hierarchical dependencies are in place provides a build system that is easy to maintain and consequently produces consistent results.
This directory holds all external libraries. The build scripts reference this directory structure during compilation. When assembling a distribution, libraries required at runtime are copied from this directory as needed.
All output from the source directory is directed to the build directory. Keeping source files separate from built files is important for good housekeeping. A clean separation of the two directories means the build directory is disposable and can simply be deleted at any time. This makes the writing of the clean target a trivial task.
The structure of the build directory should mimic that of the source for consistent navigation of all built files. It is considered good practice to direct all output from the build process to the build directory, including output from code generators such as deployment descriptors and remote interfaces. The simple rule to follow is to place all output that will be created each time a full build is run under the build directory, regardless of whether the output is source or binary.
Figure 12-5 illustrates the structure of the build directory based on the project structure defined for the source in Figure 12-4.
Figure 12-5. The build directory structure.
Having built all the necessary parts of the project, the next step is to package all components ready for deployment to the application server. For this task, we make use of the distribution directory.
The distribution directory is convenient for packaging and deployment purposes. It is possible to undertake all of this work within the build directory, but having a separate directory for all packaged files simplifies the process of generating formal releases or passing new versions of the system onto a formal test environment.
Figure 12-6 depicts an example of a distribution directory. Again this directory structure adopts the module structure seen in both the source and build directories.
Figure 12-6. The distribution directory.
Having the distribution directory fall under the main project directory is optional. For development purposes, you may wish to generate the packaged files directly into the directory structure of the application server to take advantage of its hot deployment capabilities. In some cases, the application server may call for all modules to be deployed in an uncompressed format, bypassing the need for extensive packaging.
The project structure outlined is a guideline only. The requirements of your project dictate the workings of your build process, and you should design a project structure according to your specific needs.