Home > Java, JavaScript, dojo > Creating custom dojo builds in maven

Creating custom dojo builds in maven

June 25th, 2009 Tom

We make extensive use of the dojo toolkit in our Java web applications. An important step in the development process of a dojo web application is creating a custom dojo build.  This post describes how we create custom dojo builds as part of our maven build process.

Why custom dojo builds

We use a lot of dijits and custom built dojo components in our web applications.  By consequence, you can easily end up with a web application that uses dozens of components. In a non-cross domain dojo setup, each component is downloaded synchronously in order not to break the component dependency graph. No need to explain that your application will load slowly if you don’t take extra precautions (slow script error, anyone?).  A custom dojo build optimizes the load and execution time of a dojo application (more details can be found in the dojo documentation).

We build, test, package and deploy our Java (web) applications with maven, and creating a custom dojo build is part of that build process.

Our maven approach

Preparing your maven project for a custom dojo builds consists of three steps:

  1. Obtaining a dojo source distribution
  2. Unpacking the source distribution
  3. Running the build script in the obtained dojo source distribution

Step 2 and 3 require configuring a plugin in the project’s POM file. We only use standard maven plugins, i.e. plugins that exist in the maven repository.  We did not write custom plugins.

After executing these steps, you can do two things:

  1. Build a war file that contains a web application with a dojo production build
  2. Prepare Eclipse configuration files to run the application on a WTP enabled Eclipse IDE

The rest of this post describes the 5 points outlined above in greater detail. The build process is parametrized using parameters in the maven POM file. Each parameter is explained at the end of the post.


1. Obtaining a dojo source distribution

To be able to make a custom dojo build, you need a source distribution of dojo. Unfortunately, dojo source distributions are not released to the main maven repository. There are two options:

  1. Download the source distribution from the dojo download page using the wagon-maven-plugin. However, this plugin downloads the dojo source distribution each time you preform a maven build. Not very efficient.
  2. Download the dojo source build and install it manually in your local repository, or a maven repository server hosted by your own organization. Obtaining the source distribution of dojo then boils down to adding a maven dependency for the source distribution (explained in Step 2).

We only describe the second option. As soon as dojo source releases are also deployed to the main maven repository, your maven build process will download the source release automatically, provided the group and artifact ID are the same as the ones used in this post.

The command we use to install a dojo source distribution in our local maven repository is (for version 1.3.1):

mvn install:install-file \
  -DgroupId=org.dojotoolkit \
  -DartifactId=dojo \
  -Dversion=1.3.1 \
  -Dpackaging=zip \
  -Dclassifier=sources \
  -Dfile=./dojo-release-1.3.1-src.zip \
  -DgeneratePom \
  -DcreateChecksum

To install the source distribution to a remote maven repository server, you must use the deploy plugin instead (mvn deploy:deploy-file).


2. Unpacking the source distribution

Before we can make a custom build, we must first unpack the dojo source distribution. Now that we have the source distribution in our maven repository server/local maven repository, this can be done easily with the maven-dependency-plugin. Adding the following piece of XML to the <build> section of your POM file unpacks the source distribution during the generate-sources phase of the maven build lifecycle. The dojo version you want to use and the location to which the sources are extracted are configurable with the dojo.version and the webresources.javascript.location parameter respectively.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>unpackDojo</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>unpack</goal>
      </goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>org.dojotoolkit</groupId>
            <artifactId>dojo</artifactId>
            <classifier>sources</classifier>
            <version>${dojo.version}</version>
            <type>zip</type>
            <outputDirectory>
              ${webresources.javascript.location}
            </outputDirectory>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>
  </executions>
</plugin>

We also rename the directory name of the unpacked directory from the verbose “dojo-release-<version>-src” to the more convenient “dojo-<version>” (the name you want to give is configurable through the dojo.source.local.unpacked.location parameter):

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <id>move</id>
      <configuration>
        <tasks>
          <move 
            file="${webresources.javascript.location}/${dojo.source.basename}"
            tofile="${dojo.source.local.unpacked.location}" />
        </tasks>
      </configuration>
      <phase>process-sources</phase>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>


3. Running the dojo custom build script

Once the sources are unpacked, we run the custom build script by means of the exec-maven-plugin. The plugin runs the build script in the Rhino JavaScript interpreter, which is included in the dojo source distribution. Again, some POM parameters can be configured to guide the build process. The POM parameters can be mapped directly to the parameters that can be passed to the dojo custom build script.

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
 
  <configuration>
    <executable>${java.home}/bin/java</executable>
    <workingDirectory>
      ${dojo.source.local.unpacked.location}/util/buildscripts
    </workingDirectory>
    <arguments>
      <argument>-classpath</argument>
      <argument>../shrinksafe/js.jar:../shrinksafe/shrinksafe.jar</argument>
      <argument>org.mozilla.javascript.tools.shell.Main</argument>
      <argument>build.js</argument>
      <argument>profileFile=${dojo.build.profileFile}</argument>
      <argument>action=${dojo.build.action}</argument>
      <argument>version=${dojo.build.version}</argument>
      <argument>releaseName=${dojo.build.release.name}</argument>
      <!-- releaseDir: don't forget the slash at the end! -->
      <argument>releaseDir=${dojo.build.release.location}/</argument>
      <argument>optimize=shrinksafe</argument>
      <argument>layerOptimize=shrinksafe</argument>
      <argument>internStrings=true</argument>
    </arguments>
  </configuration>
 
  <executions>
    <execution>
      <id>dojo-custom-build</id>
      <phase>compile</phase>
      <goals>
        <goal>exec</goal>
      </goals>
    </execution>
  </executions>
</plugin>


4. Building the WAR

After creating the custom dojo build, it must now be added to you web application’s WAR. This must be done by configuring the maven-war-plugin. The files created by the custom build must be added as additional web resources. The following POM excerpt adds additional web resources that are located in a directory defined by the webresources.location parameter. We put all generated web resources in that directory, which includes the files that are generated by the custom build process. Note that the unpacked dojo source distribution, which is in fact also a generated resource, is excluded from the war.

<build>
  ...
  <plugins>
    ...
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.1-beta-1</version>
      <configuration>
        <webResources>
          <resource>
            <!-- this is relative to the pom.xml directory -->
            <directory>${webresources.location}</directory>
            <excludes>
              <exclude>**/${dojo.source.friendlyname}/**</exclude>
            </excludes>
          </resource>
        </webResources>
      </configuration>
    </plugin>
     ...
  </plugins>
  ...
</build>


5. Generating eclipse project files

If you configure your POM file as described above, you can generate WTP compliant eclipse project files with the maven-eclipse-plugin.

mvn -Dwtpversion=2.0 -DdownloadSources=true -DdownloadJavadocs=true \
    eclipse:clean eclipse:eclipse

The maven-eclipse-plugin runs in the generate-resources phase of the maven lifecycle. The unpacking of the dojo source distribution is done in the generate-sources and process-sources phases of the maven build lifecycle, which both occur before the generate-resources phase (for more information, see the maven documentation on the build lifecycle.

Also, since version 2.6 of the maven-eclipse-plugin, all web resources that are defined in the maven-war-plugin section of your POM file are also added to the eclipse project. By consequence, the dojo source distribution you are using in your project is automatically deployed to the server that you’re using in your eclipse IDE for testing and debugging purposes.

Note that creating the custom build is done in the compile phase of the maven lifecycle, which occurs after the generate-resources phase. This means that the custom build won’t be available in your eclipse IDE unless you first compile the project using maven and refresh your project. The prerequisite for this to work is that the custom build must be placed somewhere inside the web resources directory that you have defined in the maven-war-plugin section.


POM Parameters

In this post we have made extensive use of POM parameters. This section enumerates and explains all the parameters we’ve mentioned in earlier sections

<properties>
  <!-- files and directory locations -->
  <webresources.location>
    ${project.build.directory}/WebResources
  </webresources.location>
  <webresources.javascript.location>
    ${webresources.location}/js
  </webresources.javascript.location>
  <dojo.version>1.3.1</dojo.version>
  <dojo.source.basename>dojo-release-${dojo.version}-src</dojo.source.basename>
  <dojo.source.friendlyname>dojo-${dojo.version}</dojo.source.friendlyname>
  <dojo.source.local.unpacked.location>
    ${webresources.javascript.location}/${dojo.source.friendlyname}
  </dojo.source.local.unpacked.location>
 
  <!-- dojo custom build parameters -->
  <dojo.build.profileFile>
     ${basedir}/facebookdemo.profile.js
  </dojo.build.profileFile>
  <dojo.build.action>clean,release</dojo.build.action>
  <dojo.build.version>${project.artifactId}-${project.version}</dojo.build.version>
  <dojo.build.release.name>dojo-release</dojo.build.release.name>
  <dojo.build.release.location>
    ${webresources.javascript.location}
  </dojo.build.release.location>
</properties>
  • webresources.location: the location where all generated webresources will be located. Since these resources are generated, they will end up in the target directory of your maven project
  • webresources.javascript.location: the location where all generated javascript resources can be found. This location is relative to the webresources.location; in this example in the js subdirectory.
  • dojo.version: the version of dojo you’re using in this maven project
  • dojo.source.basename: the name of the directory that is created when you unpack the dojo source distribution. This is typically “dojo-release-<dojo.version>-src”.
  • dojo.source.friendlyname: a less verbose name for the directory that holds the dojo source distribution. Here it is “dojo-<dojo.version>”
  • dojo.source.local.unpacked.location: this is the fully qualified location of the dojo source distribution in the maven project. This defines a directory with the name configure in dojo.source.friendlyname in the javascript webresources directory

The remainder of the parameters is used to parametrize the dojo build script. For more information on custom builds, please see the dojo documentation on custom builds.

  • dojo.build.profileFile: the location of the javascript file that contains the build profile
  • dojo.build.action: what actions must be performed in the dojo build process. This is typically “clean, release”
  • dojo.build.version: the version this dojo custom build will carry. We use a combination of the project’s artifact ID and version
  • dojo.build.release.name: How will the release be named. This corresponds with the name of the directory in which the custom build will be placed
  • dojo.build.release.location: the location where the custom build will be created. In this location a directory will be created with the name that you’ve configured in the dojo.build.release.name parameter.

There are obviously more parameters that you can pass to the build script, feel free to add your own.

Concluding remarks

It may require some tinkering before you get things working, or before things work the way you want. In the end, it’s a bit of juggling with files to get them all in the correct location. But once you’re there, you get dojo custom builds for free each time you do a maven build… provided you create a correct dojo build profile of course.

  1. Bernard Perchman
    August 31st, 2009 at 05:45 | #1

    This is an excellent solution, but a couple of enhancements and corrections are in order:

    1. I’ve created 2 poms for this project. The first pom builds a war file that has the full dojo libraries on offer, so that developers can start out without needing the complexity of the ant build. I’ve added the dojo release distribution instead of the source (it’s probably a good idea to rename the artifact id from ‘dojo’ to ‘dojo-src’ or ‘dojo-release’ accordingly). With a little bit of config overlays, you should be able to produce a common set of js files which can either run against this remote resource or use the bottled up libraries. This helps maintain a clear distinction between the vendor libraries and the apps/libraries written for your application.

    2. I’ve used maven profiles to break the tasks above into download, shrinksafe and build-war. The profiles are driven by the presence or absence of files, and can be overridden with a -P argument.

    3. for the shrinksafe plugin, you may want to set the executable to just java (ie java) so that you don’t have issues with running a jdk or jvm.

    4. In Windows environments you need a “;” instead of a “:” to separate the names of the jar files.

    5. In the war task, you probably want to go with rather than tags, because the shrinksafe job creates known file names.

    6. I built with 1.3.2. , which worked

  2. Bernard Perchman
    August 31st, 2009 at 05:46 | #2

    - point 5 should read “include” rather than “exclude”

  3. Gopal Patwa
    October 1st, 2009 at 08:20 | #3

    Dojo source code are available at this maven repository

    http://repo2.maven.org/maven2/org/dojotoolkit/

  4. October 1st, 2009 at 09:22 | #4

    @Gopal Patwa
    True, that’s where dojo deploys their production builds of dojo. However, the dojo foundation does not deploy source distributions to the maven repository (yet). The main difference between a production build and the source distribution is that most (if not all) javascript files have been processed with shrinksafe.

    All build tools, including shrinksafe but also the D.O.H, jsdoc tools, … ) have been removed from the production builds that dojo deploys to the maven repository. Consequently, you can not create custom builds with those dojo builds. To be able to do so, you must follow another procedure. My post describes a way to deploy a source distribution to your own (or local) maven repository. Once the dojo foundation releases source distributions to the main maven repositories, you’ll be able to use repo1 or repo2 to get the source distribution.

  5. December 10th, 2009 at 13:01 | #5

    Surely this repo could be used for shrinksafe?

    http://repo1.maven.org/maven2/org/dojotoolkit/custom_rhino/

  6. December 11th, 2009 at 14:36 | #6

    @Al Briggs

    OK I’ve realised the custom_rhino allows you to run rhine – but you still need build.js etc available.

    I hope a version of Dojo with sources can soon be added to maven.

  7. December 11th, 2009 at 15:35 | #7

    @Al Briggs
    Yes, a custom dojo build is a lot more than Shrinksafe. It builds a dependency tree for your application and copies all files in the dependency tree in the correct order in a single file (called a layer). The build.js file determines the layer configuration (because you can define more than one layer in your application). You can choose to optimize the code in a dojo specific way, e.g. interning a dijit templates. Running Shrinksafe on the resulting layer is also an optional optimization step in your build process.

    Moreover, in dojo version 1.4 they’ve added support for the google closure compiler ( http://code.google.com/closure/ ). So now you can choose whether you use Shrinksafe or Closure to optimize your code. I haven’t been able to try it yet though.

  8. Ben
    February 18th, 2010 at 02:39 | #8

    Maven has a path separator property (i.e, ${path.separator}) which you can use to make the build file work on both Unix and Windows.
    So, for Step 3 you should
    Replace: ../shrinksafe/js.jar:../shrinksafe/shrinksafe.jar
    With: ../shrinksafe/js.jar${path.separator}../shrinksafe/shrinksafe.jar

Comments are closed.