Project

General

Profile

Building Converted Code

As a source-to-source conversion technology, FWD produces Java source code as its end product. That Java source code must be compiled into Java byte code in order to run within a Java Virtual Machine (JVM). The Java source code emitted by the conversion process references many entry points into the FWD runtime environment, and will have dependencies upon standard Java class libraries, as well as upon third party libraries. Accordingly, the build process must take into account these linkages. The FWD project does not provide a specific tool to build your project, with the expectation that you will use existing tools available to the Java community.

Ant (ant.apache.org) is a feature rich tool set used to build many Java projects (it is what is used to build the FWD technology itself), and it is well suited for FWD conversion projects. We recommend using it as a starting point to set up a build environment for your own conversion project. This chapter discusses various considerations and requirements when creating a build environment for a new project. It assumes Ant is the tool set in use. If you use a different build tool, you should be able to adapt these concepts to the tool set of your choice.

Configuring the Build Process

Ant relies on an XML configuration file, which describes what the build tool should do when it is told to execute a set of tasks. Each such set of tasks is known as a target in Ant parlance. By default, the configuration file is named build.xml and is located in the project root directory. We follow this convention here. The build.xml file contains a number of sections, one to describe each target, and additional sections to define common variables and other shared information. Each public target can be executed directly from the command line (e.g., ant compile), or indirectly, as a dependency of other targets.

Ant provides extensive documentation and a well-commented, sample build.xml template which you can use to configure your own project's build process. Accordingly, we will not cover every detail of that file here, nor explain the meaning of every build.xml element and option, as this document is not intended as a substitute for Ant's user documentation. Rather, this section will focus on those elements of build.xml which commonly are necessary for FWD conversion projects. The intention is to present these items in a broadly applicable way, but as Ant has numerous tools available to specify your build configuration, you may discover various methods that are more suitable to your specific environment and needs. Please consult the Ant user documentation if you are unfamiliar with any of the idioms or syntax used in the subsections below.

Commonly Used Paths

References to commonly used paths are necessary throughout the build configuration. Rather than hard coding these references everywhere they are used, take advantage of Ant's user definable properties. The following properties are typical for a FWD conversion project. This section usually appears early in build.xml:

<!-- ==================== File and Directory Names ======================== -->

  <property name="build.home"    value="${basedir}/build" />
  <property name="dist.home"     value="${basedir}/dist" />
  <property name="src.home"      value="${basedir}/src" />
  <property name="srcnew.home"   value="${basedir}/srcnew" />
  <property name="lib.home"      value="${basedir}/lib" />
  <property name="cfg.home"      value="${basedir}/cfg" />
  <property name="manifest.dir"  value="${basedir}/manifest" />
  <property name="p2jlib.home"   value="${basedir}/p2j/build/lib" />
  <property name="p2jsrc.home"   value="${basedir}/p2j/src" />

The basedir substitution variable references the current working directory from which each Ant target is executed. It is defined by the basedir attribute of the project XML element, which is the outermost element of the XML hierarchy in build.xml. In this case, it is assumed basedir has been set to “.”. Thus, all path properties above are defined relative to the current working directory, which corresponds with the project root directory, assuming you will be issuing ant commands from that location.

Java Classpaths

Rather than picking up the CLASSPATH environment variable, Ant uses Java classpath s defined in build.xml. This allows you to specify different classpath s, which may be useful for certain tasks. An example of this configuration follows:

<!-- ==================== Compilation Classpath =========================== -->

  <!-- Path used when compiling Java classes -->
  <path id="compile.classpath">
    <fileset dir="${lib.home}">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${p2jlib.home}">
      <include name="*.jar"/>
    </fileset>
  </path>

  <!-- Only used by special build-time tasks -->
  <path id="tools.classpath">
    <path refid="compile.classpath"/>
    <!-- add more paths or filesets here if necessary... -->
  </path>

The compile.classpath path will be used for most tasks which require a classpath. Other tasks in the configuration will refer to this path by specifying

<path refid="compile.classpath"/>

in Ant tasks which accept a path reference.

The tools.classpath path is an example of a reference back to the compile.classpath path defined above it. The tools.classpath is set up to include all paths in the compile.classpath, plus any additional paths necessary for specific build operations. This additional path is optional, and many projects will not need it. However, you might use it if, for example, you need to execute a particular build target occasionally (perhaps building a UML model from your converted project source code), for which you need to specify additional jar files or classes that you would not want in your classpath for a regular build.

Prepare Target

In order to compile your converted source code and create jar files and javadoc documentation, you will typically need to perform some preparatory work, such as creating directories, copying files, and so forth. This is the work of the prepare target. This target is rarely executed directly from the command line, but normally is executed indirectly as a dependency of another target, such as a compile target. An example follows:

<!-- ==================== Prepare Target ================================== -->

  <target name="prepare">

    <!-- 1. Create build directories -->
    <mkdir dir="${build.home}" />
    <mkdir dir="${build.home}/lib" />
    <mkdir dir="${build.home}/classes" />

    <!-- 2. Create distribution directory -->
    <mkdir dir="${dist.home}" />
    <mkdir dir="${dist.home}/docs/api" />

    <!-- 3. Copy external jars -->
    <copy todir="${build.home}/lib">
      <fileset dir="${lib.home}" />
    </copy>

    <!-- 4. Copy DTDs from source directories to equivalent locations
            in build directories -->
    <copy todir="${build.home}/classes">
      <fileset dir="${src.home}" includes="**/*.dtd" />
    </copy>

    <!-- 5. Copy DMO index DTD from FWD src to application's build -->
    <copy todir="${build.home}/classes">
      <fileset dir="${p2jsrc.home}" includes="dmo-index*.dtd" />
    </copy>

    <!-- 6. Copy all XML documents -->
    <copy todir="${build.home}/classes">
      <fileset dir="${src.home}" includes="**/*.xml" />
    </copy>

    <!-- 7. Copy ehcache config file into classpath -->
    <copy file="${cfg.home}/ehcache.xml" 
          tofile="${build.home}/classes/ehcache.xml" />

    <!-- 8. Copy hand-written Java code into (generated) permanent package
            structure -->
    <copy todir="${src.home}">
      <fileset dir="${srcnew.home}" />
    </copy>

  </target>

Let's take a closer look at each of the numbered sections above:

  1. Create build directories. This item creates build/lib and build/classes subdirectories below the project root. Converted Java source files will later be compiled into class (.class) files, which will reside in build/classes. These in turn will be jar'd into application jar files, which will be placed in build/lib, along with copies of third party jar files required by the converted application.
  2. Create distribution directory. This item creates a dist/docs/api subdirectory below the project root, where javadoc documentation will be emitted.
  3. Copy external jars. This optional item presumes any external or third party jars required by the converted application (besides those required by FWD itself) reside in the lib subdirectory off the project root. These are copied into ${build.home}/lib. If no external jars are required by the application, this item can be omitted.
  4. Copy DTDs from source directories to build directories. This optional item copies any DTDs which are in the application's source directory tree to its build directory tree, in preparation for the later jar target. The default conversion does not place any DTDs into the application's source directory tree, so this step is only necessary if the conversion has been customized to generate DTDs. Several XML files are generated during conversion which are not validated during runtime. This is likely to change in future versions of FWD, so the inclusion of this item in build.xml anticipates this future need.
  5. Copy DMO index DTD from FWD source directory to application's build directory. A file named dmo_index.xml is generated during schema conversion and is read as a runtime resource from the converted application's classpath. A DTD file (dmo-index-{version}.dtd) is used by the XML parser to validate the dmo-index.xml file. This DTD is expected to be available with the dmo_index.xml file, and so this item copies it from its location in the FWD source directory tree to the build directory tree which will be used when creating the application's jar file. More details about the dmo_index.xml file can be found in the DMO Index chapter of the _FWD Conversion Reference _book.
  6. Copy XML documents from source directories to build directories. Numerous XML documents are generated during conversion, which are needed by the application at runtime. This item copies these files from the locations at which they were created during conversion to their matching, relative locations in the build directory tree. This ensures they will be archived appropriately in the application's jar file during execution of the jar target.
  7. Copy ehcache configuration file into the build directory. FWD uses Ehcache to implement Hibernate's second level caching. An Ehcache configuration file with default settings is located in the cfg subdirectory (the file can be edited to provide custom cache settings on a per-table basis). This item copies it to the classes subdirectory of the build directory, so that once jarred, it will be found in the root of the classpath at runtime, as expected by Hibernate.
  8. Copy hand-written Java code into (generated) permanent package structure. Over time, hand-written Java classes and other source files will be added to a project to either add function or to replace generated Java source code. Such source files should not be placed in the same source directory tree as the one in which the conversion process places its generated files, because any hand-written files with the same names as generated files will be overwritten the next time conversion is run. One way to handle this is to create a parallel source directory tree (in this case represented by the srcnew.home property). Any hand-written files are placed in the parallel directory tree. This optional item copies all such custom source from the parallel tree to the same relative location in the generated source directory tree. Note that generated files with the same relative paths and file names as their hand-written counterparts will be overwritten, so careful planning is required.

Clean Target

During the conversion and development cycle, it is often necessary to clear out the results of a previous build before attempting a new build. This is done by defining a clean target:

<!-- ==================== Clean Target ==================================== -->

  <target
     name="clean" 
     description="Delete old build and dist directories">

     <delete
        failonerror="false" 
        includeEmptyDirs="true">

        <fileset dir="${build.home}"/>
        <fileset dir="${dist.home}"/>

     </delete>

  </target>

Like the prepare target, the clean target is rarely run in isolation. It is run more commonly in preparation for a full build. Note that in the form above, this target will delete the directory containing compiled Java classes (and other files copied there by the prepare or other targets), and the directory containing the project's “distribution” material (usually JavaDoc and other documentation).

This target will not remove generated and hand-written source files. This is intentional, as it is typically necessary during the development cycle to build the project much more often than it is to re-run conversion. We normally don't want to delete the output of the last conversion run every time we build. A separate target can be defined to clear the output of the conversion process if needed; this is left as an exercise to the reader. Normally such a target would involve at least the deletion of the fileset defined by the src.home property.

Compile Target

Java source files need to be compiled by the Java compiler to class files, which can be loaded and executed by the Java Virtual Machine at runtime. This is the purpose of the compile target, which uses the Ant javac directive. Under the covers, Ant invokes the Java compiler, which reads the source files from the directory specified by the srcdir attribute, and places the compiled class files in the directory indicated by the destdir attribute.

<!-- ==================== Compile Target ================================== -->

  <target name="compile" depends="prepare" 
          description="Compile Application's Java sources">

    <!-- Compile Java classes as necessary -->

    <javac srcdir="${src.home}" 
           destdir="${build.home}/classes" 
           debug="${compile.debug}" 
           deprecation="${compile.deprecation}" 
           memoryMaximumSize="2G" 
           fork="true" 
           optimize="${compile.optimize}">

        <classpath refid="compile.classpath"/>

    </javac>

    <echo message="Compiling Java Sources Done."/>

  </target>

Other settings are configured here as well, such as those for the compilation classpath, the inclusion of debug symbols, the handling of deprecated APIs, optimization, and the memory available to the Java compiler. Please consult the Ant user documentation for further details on these options.

The compile target depends upon the prepare target, which means that each time the compile target is run, the prepare target will be executed first.

Jar Target

Java applications normally are not shipped as collections of loose class files. A FWD application usually is deployed in one or more jar files. A jar file is an archive, similar to a zip file, which contains resources needed by a Java application to execute at run time. This usually contains at least the compiled Java class files which implement the application's behavior. Often, jar files also include other files, such as static XML documents, DTD or XML schema files, images, and other static resources which are updated infrequently. Typically, these are resources which change in conjunction with application code changes.

The following example of a jar target creates two jar archives from application classes and resources:

<!-- ==================== Jar Target ============================ -->

  <target name="jar" 
          depends="compile">

     <!-- business logic classes and resources -->
     <jar jarfile="${build.home}/lib/myapp.jar" 
          basedir="${build.home}/classes" 
          includes="**/com/mydomain/myapp/**
                    dmo-index*.dtd
                    ehcache.xml" 
          excludes="**/com/mydomain/myapp/ui/**" 
          manifest="${manifest.dir}/myapp.mf" 
          index="true" 
     />

     <!-- user interface classes and resources -->
     <jar jarfile="${build.home}/lib/myapp_ui.jar" 
          basedir="${build.home}/classes" 
          includes="**/com/mydomain/myapp/ui/**" 
          index="true" 
     />

  </target>

One archive (myapp.jar) contains only business logic classes and related resources. The other archive (myapp_ui.jar) contains only user interface classes and related resources. This delineation is somewhat arbitrary, and is not strictly necessary for all applications. For many applications, a single, application jar file is sufficient.

The particular delineation into a business logic archive and a user interface archive has historical roots. The FWD project initially was developed using Sun Microsystems' JDK 1.4. Conversion of the FWD pilot project application resulted in a large number of compiled Java classes (many tens of thousands), which could not be loaded from a single jar file using that version of the Sun JVM. At the time of this writing, the minimum requirement for a FWD project is JDK 1.6 or higher. It is unknown to this author whether the limitation on the number of classes in a single jar file has been lifted in versions of the Sun JVM since 1.4.

Whether or not this particular limitation still exists, developers may have various reasons for splitting their applications into multiple jar archives. Accordingly, the example above illustrates this approach.

JavaDoc Target

The javadoc target creates API documentation for the converted application's Java classes. The current version of FWD only generates JavaDoc documentation for the Data Model Object (DMO) classes it creates based on the legacy schemas used by the original, Progress application. However, you may have additional, hand-written classes which contain such documentation. The following example creates JavaDoc documentation under the directory represented by the dist.home property. Note that for very large projects, running this target may take a long time, and may require more memory than is specified here.

<!-- ==================== Javadoc Target ========================== -->

  <target name="javadoc" 
          depends="compile" 
          description="Create Javadoc API documentation">

    <javadoc sourcepath="${src.home}" 
             destdir="${dist.home}/docs/api" 
             maxmemory="256M" 
             access="private" 
             Footer="Copyright (c) 2010, My Organization, Inc.
                     <br>
                     ALL RIGHTS RESERVED. Use is subject to license terms." 
             Windowtitle="MyApp" 
             locale="en_US" 
             packagenames="*">

      <classpath refid="compile.classpath"/>

    </javadoc>

  </target>

Schema Generation Target

The following is documentation on a deprecated feature. The recommended approach no longer requires this section. As such, adding it is optional unless using the Alternate Approach to DDL Generation is being used as documented in the Data Migration chapter.

The schema target is needed infrequently, and it is not executed as part of the regular, application build cycle. This target generates the Data Definition Language (DDL), which you will use to apply a schema to a new, PostgreSQL database. This is necessary when migrating application's data from a Progress database. Details on running this target are provided in the Data Migration chapter.

<!-- ============= Hibernate Schema Generation Target ============= →

<!--
  The "schema" target uses Hibernate ORM mapping documents to generate
  Database Definition Language (DDL) statements to create the matching
  schema in a PostgreSQL database.  The schema is not actually applied to
  the database directly;  the DDL is emitted but not executed.

  To generate the DDL for a particular database, invoke this target, passing
  the database name to ant as a property named 'target.db'.  For instance:

      ant -Dtarget.db=mydb schema

  FWD runtime environment is a prerequisite.
-->

  <target name="schema" 
          depends="prepare" 
          description="Generate DDL from hibernate mapping files">

    <taskdef name="schema-gen" 
             classname="org.hibernate.tool.hbm2ddl.SchemaExportTask" 
             classpathref="tools.classpath" />

    <schema-gen config="${cfg.home}/hibernate.cfg.xml" 
                quiet="yes" 
                text="yes" 
                drop="no" 
                output="schema-create-${target.db}.sql" 
                delimiter=";">

      <fileset dir="${build.home}/classes">
        <include name="**/${target.db}/impl/*.hbm.xml"/>
      </fileset>

    </schema-gen>

  </target>

The schema target requires as input Hibernate ORM documents, one of which is generated for each DMO type during the conversion process. Accordingly, this target depends upon the prepare target, which copies these documents to a location from which they can be used by the schema target (i.e., within the tools.classpath defined earlier).

Running the Build

Ant provides a very flexible build environment, and you may choose to modify the above configuration elements to better suit your needs. Your project may call for additional properties and new build targets. However, the above elements comprise the basic set of build tasks required by any FWD conversion project. Put them together in a build.xml file, place that file in the project root directory, and you will be ready to build your project.

To compile your converted (and hand-written) Java classes, switch to the project root directory, and run the following from the command line:

ant compile

This is a good first step to attempt after early conversions of a new project. For a complex project, it is not uncommon to encounter compilation errors initially, even though the conversion process completed without reported errors. There are various reasons for this, which may require changes to your 4GL source code, the addition of conversion hints, or fixes or enhancements to the FWD conversion technology itself. The iterative process of working through these errors is discussed in the chapter entitled Getting Your Project to Compile.

Once your project compiles without error, you will want to create jar archives, which will allow you to run your application. For this, execute:

ant jar

You may optionally want to generate JavaDoc API documentation for your project:

ant javadoc

You also can specify multiple targets to run in sequence. For example, a useful combination is to run the clean target before a jar, as in:

ant clean jar

This will remove all output of the previous build from the file system, ensuring the current build starts from a known, clean state. This is especially important in the event Java source files or other resources have been removed from the project, so obsolete class files and resources do not end up in the application's jar files.

Summary

This chapter recommended the Ant tool set to build your converted application, and discussed the minimum configuration elements necessary for a typical FWD conversion project. We reviewed common build commands. At this point, you should be ready to put together a basic build configuration and begin compiling your converted source code. Newly converted Java source code might not immediately compile cleanly. Strategies for dealing with compilation errors are discussed in the chapter Getting Your Project to Compile.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.