Tutorial #2: Escort

Escort is a step-by-step wizard for building a simple DDMS Resource from scratch, and then saving it to a file. The source code for this application shows an example of how the Java object model can be built with basic data types (possibly mapped from a database table or some other pre-existing entity).

I have implemented this wizard as a series of textual prompts, to avoid the overhead of having a full-fledged MVC Swing application (or implementing it as a web application and requiring a server to run). It is not as flashy, but this should make it easier to focus on the important sections of the source code.

This wizard only focuses on small representative subset of the DDMS components, such as Identifier and SubjectCoverage. It will not ask you for every single optional component in the specification.

Getting Started

Escort can be run from the command line with the class, buri.ddmsence.samples.Escort. The application does not accept any command line parameters.

Please see the "Getting Started" section for classpath details and command line syntax.

Walkthrough

When you run Escort you will see a simple text screen:

Escort: a DDMSence Sample

This program allows you to build a DDMS 5.0 assertion from scratch using a
representative subset of possible components. Suggested valid answers are
provided in square brackets for each prompt. However, these are not default
values (hitting Enter will answer the prompt with an empty string).

Would you like to save time by using dummy security attributes, Unclassified/USA, throughout the assertion? [Y/N]:

Figure 1. Starting the Wizard

The wizard will walk you through various components of a DDMS Resource in the order that they are found in the schema. Each component you create must be valid before you can proceed to the next one. You can optionally use dummy values for the security classification and ownerProducer attributes to save time while walking through the wizard.

First, let's try creating an invalid Identifier. The DDMS specification states that the qualifier must be a valid URI. Type in ":::::" as a qualifier and "test" as a value.

=== ddms:metacardInfo (exactly 1 required) ===
A minimal metacardInfo consist of an identifier, dates, and a publisher.
Please enter the qualifier [testQualifier]: :::::
Please enter the value [testValue]: test
[ERROR] /ddms:identifier: Invalid URI (Expected scheme name at index 0: :::::)
Please enter the qualifier [testQualifier]:

Figure 2. Outsmarting the Wizard

Because the qualifier was an invalid URI, the wizard printed out the error message and then restarted the loop to get your input values. The value, "/ddms:identifier" tells you which component was causing a problem and can be retrieved via getLocator() on the InvalidDDMSException. The format of the locator will be an XPath string, but should be enough to help you locate the offending component even if you have no XPath experience. (Skilled XPath developers will notice that ddms:identifier seems to be the root node in the string -- this is because it had not been added to the parent Resource at the time of the exception).

Now, let's take a look at the source code in /src/samples/buri/ddmsence/samples/Escort.java to see how this was accomplished. The run() method defines the control flow for the program, and uses the helper method, inputLoop(), to ask for user input until a valid component can be created:

printHead("ddms:metacardInfo (exactly 1 required)");
getTopLevelComponents().add(inputLoop(MetacardInfo.class));

Figure 3. Excerpt from the run() method

private IDDMSComponent inputLoop(Class theClass) throws IOException {
   IDDMSComponent component = null;
   while (component == null) {
      try {
         component = CONSTRUCTOR_BUILDERS.get(theClass).build();
      }
      catch (InvalidDDMSException e) {
         printError(e);
      }
   }
   return (component);
}

Figure 4. Source code to continuously loop until a valid component is created

IDDMSComponent is the identifying interface for any DDMS components implemented as Java objects, and contains methods which are common to all components. An anonymous implementation of IConstructorBuilder is created (in the Escort constructor) for each DDMS Component class, and each Builder is responsible for one top-level component. For example, here is the definition of the Builder that builds Identifiers:

CONSTRUCTOR_BUILDERS.put(Identifier.class, new IConstructorBuilder() {
   public IDDMSComponent build() throws IOException, InvalidDDMSException {
      String qualifier = readString("the qualifier [testQualifier]");
      String value = readString("the value [testValue]");
      return (new Identifier(qualifier, value));
   }		
});

Figure 5. An anonymous builder that can build Identifiers from user input

As soon as inputLoop() receives a valid component from a Builder, it returns that component to the main wizard method, run(). The main wizard saves the component in a list and moves on to the next component type. You can examine the constructor to see the code needed to build each type of component. Some Builders, like the one for a MetacardInfo will use other Builders to create child components.

Let's use the wizard to create a valid Resource. You should be able to follow the prompts to the end, but if not, the output below is one possible road to a valid Resource.

Would you like to save time by using dummy security attributes, Unclassified/USA, throughout the assertion? [Y/N]: y

=== ddms:metacardInfo (exactly 1 required) ===
A minimal metacardInfo consist of an identifier, dates, and a publisher.
Please enter the qualifier [testQualifier]: testQualifier
Please enter the value [testValue]: testValue
Please enter the created date [2010]: 2010
Please enter the posted date [2010]: 
Please enter the validTil date [2010]: 
Please enter the infoCutOff date [2010]: 
Please enter the approvedOn date [2010]: 
Please enter the receivedOn date [2010]: 
Please enter the publisher entity type [organization]: organization
Please enter the number of names this organization has [1]: 1
Please enter the number of phone numbers this organization has [0]: 0
Please enter the number of email addresses this organization has [0]: 0
Please enter the number of suborganizations to include [0]: 0
Please enter entity name #1 [testName1]: Defense Information Systems Agency
Please enter the Organization acronym [testAcronym]: DISA

=== ddms:identifier (at least 1 required) ===
Please enter the qualifier [testQualifier]: testQualifier
Please enter the value [testValue]: testValue

=== ddms:title (at least 1 required) ===
Please enter the title text [testTitle]: testTitle

=== ddms:description (only 1 allowed) ===
Include this component? [Y/N]: n

=== ddms:dates (only 1 allowed) ===
Include this component? [Y/N]: n

=== Producers: creator, publisher, contributor, and pointOfContact (at least 1 required) ===
Please enter the producer type [creator]: creator
Please enter the entity type [organization]: organization
Please enter the pocType [DoD-Dist-B]: 
Please enter the number of names this organization has [1]: 1
Please enter the number of phone numbers this organization has [0]: 0
Please enter the number of email addresses this organization has [0]: 0
Please enter the number of suborganizations to include [0]: 0
Please enter entity name #1 [testName1]: Defense Information Systems Agency
Please enter the Organization acronym [testAcronym]: DISA

=== ddms:subjectCoverage (at least 1 required) ===
Please enter the number of keywords to include [1]: 1
Please enter the number of categories to include [0]: 0
Please enter the number of productionMetrics to include [0]: 0
Please enter the number of nonStateActors to include [0]: 0
* Keyword #1
Please enter the keyword value [testValue]: ddmsence

=== ddms:resource Attributes (all required) ===
Please enter the Resource compliesWith [DDMSRules]: DDMSRules
The DDMS Resource is valid!
No warnings were recorded.

Figure 6. Successful run of the Escort Wizard

DDMSence stores warning messages on each component for conditions that aren't necessarily invalid. Calling getValidationWarnings() on any component will return the messages of that component and any subcomponents. In this run-through, no warnings were recorded. We will try an example with warnings later.

The final step is to save your valid Resource as an XML file. Enter a filename, and the Resource will be saved in the data/sample/ directory.

=== Saving the Resource ===
Would you like to save this file? [Y/N]: y
This Resource will be saved as XML in the data/sample/ directory.
Please enter a filename: test.xml
File saved at "C:\projects\ddmsence\data\sample\test.xml".

You can now open your saved file with the Essentials application.
The Escort wizard is now finished.

Figure 7. Saving the File

Once the file is saved, you can open it with the Essentials application to view the Resource in different formats. You can also use the wizard to generate additional data files for the Escape application.

If you were to run Escort again, and then create an empty ddms:dates component, you would see a warning message when the Resource was generated:

=== ddms:dates (only 1 allowed) ===
Include this component? [Y/N]: y
Please enter the created date [2010]: 
Please enter the posted date [2010]: 
Please enter the validTil date [2010]: 
Please enter the infoCutOff date [2010]:
Please enter the approvedOn date [2010]:

[...]

The DDMS Resource is valid!
The following warnings were recorded:
   [WARNING] /ddms:resource/ddms:dates: A completely empty ddms:dates element was found.

Figure 8. Triggering a Warning Condition

As you can see, the locator information on warnings is in the same format as the information on errors. Because parent components claim the warnings of their children, a more detailed locator can be created. In this case, calling getValidationWarnings() on the Resource shows the full path of "/ddms:resource/ddms:dates". If you called getValidationWarnings() on the Dates component itself, the locator would be "/ddms:dates".

If a parent-child hierarchy has some DDMS elements which are not implemented as Java objects, the locator string will include every element in the hierarchy. For example, a warning in a ddms:medium element (in DDMS 2.0, 3.0, or 3.1) will have a locator value of "/ddms:resource/ddms:format/ddms:Media/ddms:medium" even though ddms:Media is not an implemented component.

Validation for a Resource built from scratch proceeds in the following order:

Conclusion

In this tutorial, you have seen how DDMS Resources can be built from scratch. You have also seen further examples of component validation. In practice, the data-driven constructor approach for building components is somewhat inflexible. The Component Builder framework offers more flexibility in the building process, and can also be used to edit existing DDMS Resources. More about this framework can be found in the Power Tip on Using Component Builders.

The next tutorial, covering the Escape application, will show how a DDMS Resource can be traversed and used in other contexts.

Tutorial #1: Essentials
Tutorial #2: Escort (you are here)
Tutorial #3: Escape
Back to Samples Documentation