You are on page 1of 53

Naval Oceanographic Office

Systems Integration Division Stennis Space Center, Mississippi 39522-5001

DS-UG-20080506

USER GUIDE
NAVY METOC DATA SERVICES FRAMEWORK (NMDSF)

NMDSF VERSION 1.3 JMBL VERSION 3.27

Prepared by: Naval Oceanographic Office Systems Integration Division Stennis Space Center, MS

May 2008

APPROVED FOR PUBLIC RELEASE/RELEASABLE TO THE INTERNET

This page intentionally left blank

Table of Contents
1Scope...............................................................................................................................................................1 Purpose..........................................................................................................................................................1 System Overview..........................................................................................................................................1 Document Overview.....................................................................................................................................1 Client Web Application.....................................................................................................................................1 Purpose..........................................................................................................................................................2 General Usage...............................................................................................................................................2 Step 1: Location and Initial Time Filtering.............................................................................................2 Step 2a: Choosing a Data Asset...............................................................................................................5 Step 2: Refining the Criteria....................................................................................................................6 Step 3: View or Download Results..........................................................................................................9 Using JMBL and WSDL.................................................................................................................................10 Background.................................................................................................................................................11 WSDL Introduction.....................................................................................................................................11 1.1Schemas..................................................................................................................................................12 Parameters...................................................................................................................................................12 JMBL Examples..........................................................................................................................................12 Point Request and Response...................................................................................................................13 Grid Request and Response....................................................................................................................15 Service Discovery and Access....................................................................................................................16 2Accessing Data Using Web Services............................................................................................................16 Apache Axis................................................................................................................................................17 Generating Stubs.....................................................................................................................................17 2.1.1Generating Skeletons......................................................................................................................19 Query by Spatial Vector.........................................................................................................................19 Query by Bounding Box.........................................................................................................................22 Castor...........................................................................................................................................................25 JMBL Class Extraction...........................................................................................................................26 Run-time Configuration..........................................................................................................................27 Bean Descriptor Cache...........................................................................................................................28 Serialization and Deserialization............................................................................................................29 JMBL Request Templates.......................................................................................................................31 Obtaining Data by Polling......................................................................................................................36 Microsoft .NET...........................................................................................................................................37 Generating Stubs.....................................................................................................................................37 Schema Changes.....................................................................................................................................38 Query by Spatial Vector.........................................................................................................................38 Appendix A: Joint METOC Broker Language WSDL.................................................................................41 Appendix B: MarshallerDemarshaller Class.................................................................................................42 Appendix C: NMDSF Poller..........................................................................................................................49 Query by Process.........................................................................................................................................49 Query by Serialized Request.......................................................................................................................50 Query by Product........................................................................................................................................50

ii

1 Scope
Purpose
This user guide provides an overview of the U.S. Navys Meteorological and Oceanographic (METOC) Data Services Framework, its associated tools, and the access techniques needed to obtain METOC data.

System Overview
The Navy METOC Data Services Framework (NMDSF) is a collection of tools for METOC data providers and consumers to send and receive METOC data. Since 1995, the Joint METOC Data Standardization Working Group (currently renamed to the Joint METOC Data Management Working Group) has defined the Joint METOC Broker Language (JMBL). JMBL is a platform-neutral web service schema for communicating both requests and responses for METOC data. A primary objective of the board is to describe JMBL expressions used by METOC producers and consumers throughout the US Government. JMBL incorporates the concerns of the entire METOC community of interest (COI) to enable data transfer and render one-off designs of the past obsolete. NMDSF is a collection of web services which use the JMBL specifications. It contains default implementations for distributing Network Common Data Form (NetCDF1), Gridded Binary (GRIB2), and Relational Database Management System (RDBMS) data. The primary component in NMDSF is the Data-Oriented Services (DOS) Framework. The framework is a toolkit for deploying data-oriented services that securely deliver geospatial information consistent with the JMBL specification. This enterprise architecture enables the deployment of different data-oriented services, on multiple platforms, linked together by configuration in a proxy. The framework provides facilities for analyzing JMBL requests, determining the best data-oriented services for answering them, and propagating responses with return values. NMDSF accepts Simple Object Access Protocol (SOAP3) Messages that conform to the JMBL specification. Data is embedded directly in the JMBL response or accessed via a Uniform Resource Locator (URL).

Document Overview
The goal of this document is to explain the integration procedures and steps needed to obtain METOC data from NMDSF servers. This document focuses on the machine-to-machine communication and message formatting required for accessing data. A human-to-machine User Interface (UI) is discussed in the next section and is useful for developers in understanding JMBL message formats. Direct machine-to-machine access is provided by using the Web Services Definition Language (WSDL4) and is further described in Section 3.

Client Web Application


The Navy METOC Data Services Client (client) is used to display point and line data as well as download gridded data files. In general, the web services in NMDSF enable sophisticated direct machine to machine interaction (e.g., discovery, direct queries, etc.). The client uses JMBL to construct requests and format or save responses from NMDSF servers. This tool provides a customizable template-driven interface for defining requests consistent with backend NMDSF servers. The client embeds data directly within the
1 2 Network Common Data Form, http://www.unidata.ucar.edu/software/netcdf/. A Guide to the Code Form FM 92-IX Ext. GRIB, Edition 1, http://www.wmo.ch/pages/prog/www/WDM/Guides/Guidebinary-2.html. 3 SOAP Specifications, http://www.w3.org/TR/soap/. 4 Web Services Description Language, http://www.w3.org/TR/wsdl.

browser (for point and line retrievals) or provides links for downloading data (for areas). It utilizes RESTstyle web services to discover the data available on NMDSF servers.

Purpose
The client is used to view formatted JMBL requests and responses according to the business rules employed at production centers or other sites. An intuitive user interface is used to define the search characteristics of data. Based on those criteria, results screens are displayed with metadata and links to download relevant data files from JMBL responses received (by reference requests) or save the raw response data (by value). This tool can also be used to save a formatted JMBL request for later reuse (or manual editing). Saved requests are useful as templates in other applications (including the client which supports loading a saved request). Using templates for JMBL requests in your own application is discussed in Section 4.2.5.

General Usage
Several global options are available in the client. These options are in the upper panel and include: 1. New Data Request By default, actions performed in the client are saved automatically as you proceed through the steps (a summary is in the left panel). To start over with a new request, click this button. Closing the browser (or a timeout on the server) will restart your session, and any unsaved data requests are lost (see below). 2. Coverage Map Displays a coverage map of all the latest data for all models. This coverage map currently does not support the ability to filter data by time, security, resolution, or other constraints. Map controls (button bar) are similar to the options described in Step 1 for map location entry. 3. Open SAVED Data Request Opens a previously saved data request from your local computer (the option to save is available on Step 3). A popup is provided to select the request from your local hard drive. The client allows data requestors to further refine and narrow their search for a specific data asset. It features a multi-step process designed to locate the data you need as you proceed. Each of these steps is now presented in greater detail. NOTE: Online help is available for many fields. Place the mouse cursor over the text or field and a text box will be displayed to provide additional context-sensitive information.

Step 1: Location and Initial Time Filtering


The first step in the client is to define the location and initial time constraints for the request. For the location, the default is to include a map display that shows the whole world. A selection may be made at the top, which includes the ability to enter the location by point, line, area, or via the map (area selection or bounding box entry). For all location entry options, the classification option allows customizing the value used for outputs. If the classification of the data is higher than the option selected, the highest value is used.

Figure 1: Client Location Entry by Point. Figure 2: Client Location Entry by Line.

Figure 3: Client Location Entry by Area.

As can be noted from Figures 1 through 3, it is possible to enter values either in decimal degrees or using degrees, minutes, and seconds. The values for area denoting upper and lower values are in reference to the Northern Hemisphere (e.g., upper latitude is toward the North Pole). Thus, the left longitude is toward the west once oriented north. Values entered should be from -180 to 180 for longitude and 90 and -90 for latitude. These correspond to the acceptable range of values in JMBL. A few notes are warranted when using the map location entry option, shown in Figure 4. With this option enabled, a small tool bar is available. The tool bar options, from left to right, are: 1. Zoom In (based on the current map extents). 2. Zoom Out (based on the current map extents). 3. Draw a Bounding Box on the map; map extents will be set based on the northwest and southeast points. An attempt is made to minimize distortion based on the standard map aspect ratio (actual values may differ as a result). 4. Draw a Bounding Circle on the map; just as with a Bounding Box, map extents will be set based on the radius and midpoint and may be adjusted based on aspect ratio. 5. Drag the map by using the mouse to left click, hold, and then move the image into a better position. 6. Reset the current map to include the whole world. 7. Undo any recent location selection and return to the previous extents.

Map Button Bar; options selected with a left-click.

Figure 4: Client Location Entry by Map.

After the location is chosen, the data type and initial filtering date may then be selected or entered. There are two options for the data type; the first (and default) option is to select a parameter. The Search Text field allows you to filter the parameter list. Type in a value in this field (e.g., temperature) and then choose the Update List Button. To return to the full list of parameters that are available, choose the Reset List Button. These options are depicted in Figure 5.

Figure 5: Client Data Type Parameter Selection.

Figure 6 illustrates the capability to query by a specific database model. If a model is chosen, all parameters associated with it will be available on subsequent steps (for further refinement). If a parameter is chosen that is in multiple models, all model results are shown in the next step.

Figure 6: Client Data Type Model Selection.

Figures 5 and 6 also demonstrate the ability to perform filtering based on date. Selecting the Latest Date option filters data based on the latest date (e.g., base reference date for analysis forecasts). Entering a date range allows retrieving of older data. The Start Date must be before the End Date. Once all location, data type, and date information is correctly entered, the Next Button may be chosen to proceed to Step 2. For this step, if the initial search criteria matched one data asset, the next section may be skipped (proceed directly to the Refining the Search Criteria Section). However, if more than one match is available the specific data asset must be chosen. This is further discussed in the next section.

Step 2a: Choosing a Data Asset


A list of results is displayed after the initial filtering is done in Step 1. An important point is now made regarding the left panel of the client. The large buttons within this panel may be clicked to proceed directly to another step (forward or backward). These buttons also display the status of current selections. An example is shown in Figure 7 after completing the first step. Options not entered have default values based on the data asset chosen. In addition, there are several other options on this panel to submit feedback and display a text-based coverage report of available data (not shown).

Figure 7: Left Option Panel Buttons.

Based on the initial criteria entered from Step 1, an example results table after an initial search is done is shown in Figure 8. This step is skipped if data assets do not rely on a time filter or resolution selection (proceed to Step 2). 5

To choose a specific asset, highlight the row within this table (with the mouse). The candidate row item is highlighted a different color and can be selected with the left mouse button.

Figure 8: Available Data Assets Selection.

Step 2: Refining the Criteria


Once the selection is made on Step 2a, it is now necessary to further refine the attributes required for the data asset chosen. This step provides the ability to filter the data based on depth, resolution, time, and parameter. If the data asset chosen does not support any of these criteria, those options are disabled or not shown. Figures 9 through 11 demonstrate the ability to filter based on depth. Ensure to select the appropriate units of measure (this list is filtered based on values supported in NMDSF).

Figure 9: Depth Selection - Include All Depths.

Figure 10: Depth Selection - Specify a Depth.

Figure 11: Depth Selection - Specify a Depth Range.

The native data resolution options are available beneath the depth entry with the default based on the data asset selected. If a resolution range is available, enter the smallest resolution that is acceptable within the 6

Finest Resolution field and the largest acceptable value in the Coarsest Resolution. Changing the resolution in this manner allows you to include more (or even exclude) the data asset chosen from Step 2a.

Figure 12: Resolution Options (Native and Output).

As shown in Figure 12, the output resolution may also be changed. The default is to produce a data output file that matches the native or input resolution of the data. If changing the default value, unselect the Use Default Output Resolutions checkbox and enter the row and column resolution values. The output resolution options are available only when choosing area or map on Step 1. After the resolution information is entered, the desired list of parameters may be refined. By default, when making an initial selection in Step 2a all available parameters are selected. An example, from the Wave Analysis Model (WAM), is provided in Figure 13. The Select All Button automatically chooses all parameters from the available list. Conversely, the Deselect All Button will unselect all parameters. For each parameter, if multiple units of measure are available a drop-down list (not shown in Figure 13) is available to choose the desired value. The native unit of measure for the stored data is the default.

Figure 13: Data Asset Parameter Selection.

After the data asset parameters are displayed, the time filters for that data asset are then shown. This time filter list is dynamic; only those filters supported for that asset are shown. The complete list of time filters supported includes Time Range, Latest Time, Modified Since Time, Forecast Time, and Time Period. An example for WAM is shown in Figure 14. The latest time filter selection option only provides data results that are the most recent. This is the default option unless a Time Range was chosen in Step 1.

Figure 14: Time Filter Selection Latest Time.

For analysis forecast data, the Forecast Time filter option is available (Figure 15). The base reference time defaults to the data asset chosen from Step 2a. The Forecast Period List represents the tau values (number of hours since the base reference time) for this data asset. Multiple values may be highlighted using the shift or control keys (left-click with the mouse simultaneously).

Figure 15: Time Filter Selection - Forecast Time.

The Time Range filter selection is shown in Figure 16. If a time range was entered in Step 1, the start and end times default to the entered initial dates (without the time component). Although a data asset was selected on Step 2a, the start and end times do not default to the asset that was selected. Choose the Back Button to obtain the previous date of any data assets needed to refine the start and end times for this time filter.

Figure 16: Time Filter Selection - Time Range.

The Modified Since Time option allows filtering data based on when data was last changed (Figure 17). Some data assets are updated on a scheduled basis. Any data asset modified after the date entered will be subsequently shown on the next step.

Figure 17: Time Filter Selection - Modified Since Time.

For climatology data assets, time period is enabled and is shown in Figure 18. The time qualifier information is dependent on data asset. The example shown below is for the Generalized Digital Environmental Model (GDEM) whose data is available by month.

Figure 18: Time Filter Selection - Time Period.

Once all the information constraints on Step 2 are entered, select the Submit Data Request Button to proceed. Internally, the client collects all the information entered to format a JMBL request. This request is then sent to the NMDSF server, and the results (JMBL response) are displayed in a human-readable form.

Step 3: View or Download Results


After refining the data asset filter selection options in Step 2, the JMBL results are formatted in the browser. If an area or map location was entered, the results look similar to Figures 19 and 20. In Figure 19, multiple parameters were chosen, which are shown within the scrollable list under the METOC Data Column. To obtain this data, click the Download link. The format shown is the default (and only) format available for this particular data asset (i.e., NCDF).

Figure 19: JMBL Query Results - Area with Multiple Parameters.

If data assets can be converted into more than one format, a drop-down list is presented in the Format Column. This list is populated in accordance with the available formats for that particular data asset. It is important to select the format PRIOR to choosing the download link. The default format is automatically selected if the download link is chosen first.

Figure 20: JMBL Query Results - Area with One Parameter and Multiple Formats.

The previous two figures covered area downloads (there may be one or more result rows available to download depending on the filter information provided in Step 2). However, if point or line requests are made, the formatted browser response is similar to Figure 21.

Figure 21: JMBL Query Results - Point with Multiple Parameters.

In Figure 21, a single point was requested. The metadata from the JMBL response is shown along with the data values in column format. Multiple depths will cause a vertical scrolling, whereas multiple parameters are shown within the columns. As can be noted in the JMBL examples section, METOC data items are recursive with a top-level data item containing a specific depth value, and resultant parameter values formatted one level (in the XML structure) beneath that. Data is shown formatted by date, resolution, and parameter values. If there is no depth associated with parameters, they are displayed in a row fashion with parameter name to the left and values to the right (layout by rows, not columns). By default, the results of the JMBL request are shown formatted in the browser. In addition to browser formatting, the same results display contains a Save JMBL Response Button. By selecting this button, the original JMBL response can be downloaded and viewed in any editor (e.g., windows notepad, etc.). This is very useful for application developers or other interested parties in understanding the raw XML output of the JMBL response from an NMDSF server.

Using JMBL and WSDL


The previous section discussed how to use the client in order to view and save Joint Meteorology and Oceanography (METOC) Broker Language (JMBL) requests and responses. Although the client is useful tool in understanding message formats, one must still perform the necessary integration in your software to access METOC data from an NMDSF server. For this effort, in-depth knowledge of the JMBL 10

specification and of web services is needed. JMBL is discussed in this section and the latter is addressed in open specifications5. JMBL was designed for the interchange of information between METOC data providers and user applications. JMBL provides a standardized interface to access information using a web service. It uses a unified schema and Community of Interest (COI) semantics to provide interoperability in a documentoriented, request and response communication paradigm. The entities defined in the Joint METOC Conceptual Data Model (JMCDM) are encapsulated in JMBL and approved through the Joint METOC Data Standards Working Group (DSWG). The public web site for JMBL information is located at http://www.cffc.navy.mil/metoc/. From this site, the latest version can be downloaded directly along with additional support information and tools.

Background
The Joint METOC Data Management Working Group (DMWG) currently addresses the need for interoperability standards between entities in the US Department of Defense and other agencies. This group was tasked with developing a common understanding of METOC terminology and data attribution. The resultant work product is the Joint METOC Conceptual Data Model, a fully documented database of geographically located METOC data. This conceptual model was segmented into physical segments to facilitate storage by subject type. These segments include JMPLAT (Platform Information), JMOBS (Conventional Observations), JMAN (Alpha/Numeric Text), JMGRID (Gridded Data), JMR-SOB (Remote-Sensed Observations), JMIM (Imagery), JMSESS (Space Environment and Solar), JMCLIM (Climatology), JMCAT (Catalog), JMVEC ( Vector-Graphics), JMSAT (Raw Satellite Data), JME (Weather Effects), and JMCOM (common items such as Security, etc.). As of July, 2007 JMVEC has been deprecated and no longer considered a segment model. In general, all Joint METOC databases are dynamically populated with perishable environmental data that can be ingested, updated, and deleted on a regular and real-time basis. External interfaces, systems, and local applications provide METOC data to multiple databases to collectively form the Virtual Joint METOC database (VJMDB). To avoid the presentation of multiple user interfaces, the JM APIs were made accessible by a user through the JMBL Web Service, a standard interface to the VJMDB. WSDL was used to create this interface although the realization of a single composite interface across all METOC data providers is not yet possible. Sites that seek METOC data through JMBL must currently use the specific endpoint address for the specific site they seek to obtain data from.

WSDL Introduction
Web Services Description Language (WSDL) provides a syntactical means (in an XML document) for describing network services as a series of endpoints operating on messages containing either documentoriented or procedure-oriented information. The operations and messages are described abstractly, and then bound to a concrete network protocol and message format to define an endpoint. Related concrete endpoints are combined into abstract endpoints (services). The distribution of JMBL includes a WSDL file. This file, and the associated schemas, is used to generate the needed information locally (stubs) in order to access a web service. In general, there are numerous tools available that may be used to connect to a web service. A sample listing of tools is included in the table below. Specific documentation for your tool should be used in order for you to send and receive messages to a web service. Apache Axis, used internally along with Castor within NMDSF, is further described in the Third-Party Applications Section later in this document.
5 Web Services Activity @ W3C, http://www.w3.org/2002/ws/.

11

Useful Tools for Web Services Tool Axis2 Language Java / C Brief Description Open Source Project from the Apache Software Foundation. Includes support for SOAP 1.1 and 1.2 and REST style web services. Suns fundamental technology for developing SOAP-based and RESTful Java Web Services. Supercedes JAXRPC in web services and web applications. Primary project that includes SOAPpy, a web services toolkit. It includes a library of WSDL and other classes useful for building Web Services stacks. Collection of Perl modules which provides a simple and lightweight interface to the SOAP on both the client and server. Microsofts managed code programming model for building applications on Windows clients, servers, and mobile or embedded devices. Web Site
http://ws.apache.org/axis2/

JAX-WS

Java

http://java.sun.com/webservices/technologi es/index.jsp

Zolera SOAP Infrastructure (ZSI) SOAP::Lite

Python

http://pywebsvcs.sourceforge.net/

Perl

http://www.soaplite.com/

.NET

C#, C++, any other CLRbased Language

http://msdn2.microsoft.com/enus/netframework/default.aspx

1.1

Schemas

The current implementation for JMBL includes a WSDL file and set of five schema files. The primary schemas are the jmblRequest.xsd and jmblResponse.xsd files. These schemas import three secondary schemas: jmblTypes.xsd, jmblAttributes.xsd, and jmblElements.xsd. The primary schemas comprise a small set of parent elements and references to secondary schemas. There is also an optional subscription schema, currently not supported in NMDSF, which consists of the primary file subscription.xsd and three secondary schemas: subElements.xsd, jmblSub.xsd, and subAtts.xsd.

Parameters
The JMBL XML schema includes the attributes parameterName, defined as the name of the environmental parameter, and compoundParameterName, defined as the name of the compound environmental parameter (a single parameter is expanded to multiple parameters). They are used to specify the environmental data to be communicated using JMBL. The Master Parameter List is the listing of all parameters supported by a machine to machine request (approximately 1300 entries). Each data provider will support one or more of these parameters.

JMBL Examples
JMBL itself can be a daunting subject for beginners. Each data asset deployed in NMDSF conforms to a number of high-level syntactical and semantic rules. A complete discussion of these rules is outside the scope of this user guide. However, it is often useful to learn with examples and the best source for these is 12

to utilize the client application as discussed earlier in this user guide. Using NCOM as a guide, a few formatted examples are also presented in this section.

Point Request and Response


The example below is a points request for salinity values on 01/17/2008 0Z for tau values 0 and 12.
<Request> <InformationType> <MetocDataType> <GriddedData> <GriddedAnalysisForecast/> <Location> <DiscreteGeometric> <SpatialVector> <SpatialPoint latitude="20.0" longitude="65.0"/> <SpatialPoint latitude="15.0" longitude="70.0"/> </SpatialVector> </DiscreteGeometric> </Location> <Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="0 12"/> </Time> <GridParameter> <Parameter parameterName="salinityWater/> </GridParameter> </GriddedData> </MetocDataType> </InformationType> </Request>

The following is a sample (invalid data) response from the previous request. For this response, it is important to note that each tau and parameter generates a new data item. Each response is also organized per depth, where the data is contained recursively at the specified depth. Only the surface depth (0) is shown for illustrative purposes, but with no depth range specified in the request, an ocean parameter query will return all depths.
<Response> <DataItem> <Format responseContainment="By value"/> <Location> <DiscreteGeometric> <SpatialVector> <SpatialPoint latitude="20.0000000" longitude="65.0000000"/> </SpatialVector> </DiscreteGeometric> </Location> <Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="0"/> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>0.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.5</FloatValue> </Value> </METOCdata> </METOCdata> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>5.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.4</FloatValue>

13

</Value> </METOCdata> </METOCdata> </DataItem> <DataItem> <Format responseContainment="By value"/> <Location> <DiscreteGeometric> <SpatialVector> <SpatialPoint latitude="20.0000000" longitude="65.0000000"/> </SpatialVector> </DiscreteGeometric> </Location> <Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="12"/> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>0.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.65</FloatValue> </Value> </METOCdata> </METOCdata> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>5.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.44</FloatValue> </Value> </METOCdata> </METOCdata> </DataItem> <DataItem> <Format responseContainment="By value"/> <Location> <DiscreteGeometric> <SpatialVector> <SpatialPoint latitude="15.0000000" longitude="70.0000000"/> </SpatialVector> </DiscreteGeometric> </Location> <Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="0"/> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>0.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.5</FloatValue> </Value> </METOCdata> </METOCdata> </DataItem> <DataItem> <Format responseContainment="By value"/> <Location> <DiscreteGeometric> <SpatialVector> <SpatialPoint latitude="15.0000000" longitude="70.0000000"/> </SpatialVector> </DiscreteGeometric> </Location>

14

<Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="12"/> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <METOCdata dataElementName="depth" parameterUnit="meters"> <Value> <FloatValue>0.0</FloatValue> </Value> <METOCdata dataElementName="salinityWater parameterUnit="partsPerThousand"> <Value> <FloatValue>16.5</FloatValue> </Value> </METOCdata> </METOCdata> </DataItem> <SecurityMarking classification="U" distributionRestriction="D"/> </Response>

Grid Request and Response


The following example illustrates an area request (BoundingBox) for NCOM temperature data. Since NCOM is an analysis forecast, the latest time is requested.
<Request> <InformationType> <MetocDataType> <GriddedData> <GriddedAnalysisForecast/> <Location> <BoundingBox lowerLeftLatitude="16.0" lowerLeftLongitude="56.0" upperRightLatitude="20.0" upperRightLongitude="60.0"/> </Location> <Time> <LatestTimeFlag>1</LatestTimeFlag> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <GridParameter> <Parameter parameterName="temperatureWater"/> </GridParameter> </GriddedData> </MetocDataType> </InformationType> </Request>

Below is a sample response from the above request. It is important to recognize the networkAddressName contains the location information needed in order to perform a subsequent request (http in this example) to obtain the data. Thus, the real data is not obtained directly from the initial JMBL response. A subsequent non-JMBL request is needed to perform the retrieval of the data at the indicated address from the initial response.
<Response> <DataItem> <Format responseContainment="By reference" distributionFormatCode="NCDF" networkAddressName="http://localhost:8080/test/gncom3DGridTest.nc"/> <Location> <BoundingBox lowerLeftLatitude="16.0" lowerLeftLongitude="56.0" upperRightLatitude="20.0" upperRightLongitude="60.0"/> </Location> <Time> <ForecastTime baseReferenceTime="2008-01-01T00:00" forecastPeriod="0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48"/> </Time> <Process> <ProcessName>NCOM</ProcessName> </Process> <METOCdata dataElementName="levelSigma" parameterUnit="nondimensional">

15

<METOCdata dataElementName="temperatureWater" parameterUnit="degreesCelcius"/> </METOCdata> <METOCdata dataElementName="depth" parameterUnit="meters"> <METOCdata dataElementName="temperatureWater" parameterUnit="degreesFahrenheit"/> </METOCdata> <SecurityMarking classification="U" distributionRestriction="S" specialDistribution="Distribution limited to Careful User Guide Readers Only"/> </DataItem> </Response>

Service Discovery and Access


A key component of a Service Enabled Environment is a registry or repository of all web services that comprise an enterprise. Such a respository provides valuable information about the author of the service, the functionality of the service, service interface details, and categorization information. This registry provides all the information needed for an application developer to locate an appropriate service; determine the features and functions of that service; identify how to invoke the service; and determine how to connect to it. The Net-Centric Enterprise Services (NCES) Discovery Service provides such a registry on both unclassified and classified networks. As of April 2008, a new Managed Service Provider solution for Service Discovery is being deployed based on the Systinet Universal Description, Discovery, and Integration (UDDI) Registry6. There are multiple environments available for Service Discovery. A business entity is registered under the Naval Oceanographic Office. Underneath this entry is the service registration for NMDSF. This information avoids point-to-point interfaces (e.g., hardcoding an access URL in your application). In a future revision of this user guide, sample code will be provided demonstrating how to use the service information in the UDDI Registry to access NMDSF. However, in the meantime you may contact the NMDSF development team to obtain additional guidance on using UDDI. Using a registry avoids having to update your application as web service endpoints change. Contacts: Project Manager Mr. Gregory Bodet Naval Oceanographic Office 1002 Balch Blvd, Code N641 Stennis Space Center, MS 39522 Phone: 228-688-5556 Email: gregory.bodet@navy.mil Mr. Henry Pugh Naval Oceanographic Office 1002 Balch Blvd, Code N642 Stennis Space Center, MS 39522 Phone: 228-688-5559 Email: henry.pugh@navy.mil

Implementation and Design Manager

2 Accessing Data Using Web Services


As briefly mentioned in the WSDL Introduction Section, toolkits are available to support the local binding (skeleton) and remote access (stub) of web services. The primary toolkit to enable the transmission and receipt of METOC data within NMDSF is Apache Axis.7 It handles the low level communication needed through flexible configuration. This toolkit supports SOAP 1.1 and 1.2 requests, in addition to the REST

6 7

NCES Service Discovery, https://www.us.army.mil/suite/page/527143. Apache Axis2, http://ws.apache.org/axis2/.

16

style. The JMBL interface currently uses SOAP 1.1 for greater interoperability. This toolkit is highly recommended in order to reduce dependence on the capabilities provided by any Java 2 Enterprise Edition (J2EE) Application Container (e.g., BEA WebLogic, IBM WebSphere, JBoss, etc.). Some vendors bundle non-conforming or enhanced routines that cannot be relied on for cross-vendor deployments. If Axis is used, even the object bindings can be easily substituted if flexibility is required during class file generation (e.g., from the WSDL and schema files).

Apache Axis
The Apache Software Foundations Axis Project is an implementation of the Simple Object Access Protocol8 (SOAP) with the W3C. It is designed to make use of modules, which extend the core functionality for features such as security and reliability (e.g., WS-ReliableMessaging, WS-Coordination, WS-AtomicTransaction, WS-Security, and WS-Addressing). It is built upon Apache AXIOM, a highperformance, pull-based XML object model. The version currently implemented in NMDSF is 1.2, which supports both WSDL 1.1 (SOAP version 2.0 support exists in Axis, but no endpoints are defined in NMDSF). Currently, the only transport implemented in NMDSF is the Secure Hypertext Transfer Protocol (HTTPS). Using the Axis toolkit, there are several different binding mechanisms available to include Axis Data Binding (ADB), XMLBeans, and JibX. The binding method chosen for NMDSF uses Castor (not bundled within Axis) and is further described in this section. This choice is largely based on backward compatibility with previous versions of NMDSF. However, given the data binding options, any method employed should enable connectivity via Java to an NMDSF Server. The examples in this documentation use XMLBeans (it generates a unique class for each object in the model by default). The remaining subsections assume you have obtained the version of JMBL employed in NMDSF (3.27) and are using the Standard Binary Distribution for Axis2 (1.2). The examples are tailored for Windows XP/2000 and rely on the following directory structure:
c:\schemas c:\axis Location of relevant JMBL schemas and WSDL Axis2 Binary Distribution

In addition to the above structure, all DOS commands assume the JAVA_HOME environment variable is set to the installation base path for the Java Development Kit (1.6). In addition, the JMBL schemas may need to have schemaLocation lines commented at the top of each XML Schema Definition File (to prevent circular references in the code generator). All commands are executed from the cmd.exe interpreter (e.g., Start->Run, then type cmd.exe for the command name). For the examples contained in this section, the following classes are required to be in the CLASSPATH: axis2-kernel-1.2.jar, axis2-xmlbeans-1.2.jar, axiom-api-1.2.4.jar, axiom-dom-1.2.4.jar, axiom-impl1.2.4.jar, xbean-2.2.0.jar, wsdl4j-1.6.2.jar, commons-codec-1.3.jar, commons-httpclient-3.0.1.jar, commons-io-1.2.jar, commons-logging-1.1.jar, activation-1.1.jar, axis2-codegen-1.2.jar, axis2-fastinfoset1.2.jar, axis2-jaxbri-1.2.jar, axis2-jaxws-1.2.jar, axis2-jaxws-api-1.2.jar, axis2-json-1.2.jar, axis2metadata-1.2.jar, axis2-saaj-1.2.jar, axis2-saaj-api-1.2.jar, axis2-tools-1.2.jar, backport-util-concurrent2.2.jar, mail-1.4.jar, and XmlSchema-1.3.1.jar.

Generating Stubs
An application that seeks to connect to a JMBL service provider must generate stub classes. These classes are used locally to provide an object representation for your application, rather than working directly with
8 Simple Object Access Protocol Specifications, http://www.w3.org/TR/soap/.

17

the XML SOAP requests. Using Axis and XMLBeans binding, stub classes can be generated with the following commands:
set JAVA_HOME=c:\jdk6 set AXIS2_HOME=c:\axis set PATH=%JAVA_HOME%\bin;%AXIS2_HOME%\bin;%PATH% wsdl2java uri services.wsdl d xmlbeans s

The above commands generate classes in the local working directory (can be overridden with the command-line option o directory). If an Input Line Size error occurs, this is likely the result of a DOS limitation on command line length. The typical default windows value is 8192 characters. This problem is resolved by using shorter pathnames for your installation directories. Using XMLBeans binding, a number of other additional files are generated in the resources directory. These files, many of them have a file extension xsb (XML Schema Binary), are needed in order to perform the data binding. Thus, these files should be included in the CLASSPATH when running applications that make use of the stubs or skeletons. There is a dumpxsb tool in XMLBeans, which can be used to display the contents of any xsb file. These files contain schema metadata needed to perform binding and validation. Some additional useful options for wsdl2java are included in the table below. A full list of options is available on the Apache Axis2 web site. Command-Line Option
-uri <Location of WSDL>

[-o | --output] <output location>

[-p | --package] <package name>

[-s | --sync]

[-ss | --server-side] [-sd | --service-description]

[-g | --generate-all]

[-u | --unpack-classes] -sn <service name>

Purpose WSDL file location. This should point to a WSDL file on the file system. Any relative path references will be taken from the base directory of this file. The output files location. This is where files are copied once the code generation is done. If omitted, the current working directory is used. The target package name. If omitted, a default package formed from the target namespace of the WSDL is used (e.g., jmbl.jmcbl.metoc in the example in this section). Generate code for synchronous style. The remote methods invoked will cause the requestor to wait for the response (if the message exchange pattern warrants it). Generate server side code. Generates the service descriptor (i.e., server.xml). This file describes how the server side components are deployed. Generates all the classes. This option is only valid with ss; when enabled client side code is also generated. When enabled, this option generates separate classes for each of the data binders. Specify the service name to be generated (multiple services in the WSDL). If not specified, the first service is used. 18

-pn <port name> [-ns2p | --namespace2package]

[-ssi | --serverside-interface]

Specify the port name to be generated. If not specified, the first port is used. Specify a comma separated list of namespaces and packages where the given package name will be used for the relevant namespace. This is useful if you want more control of package names for generated code. Generate an interface for the service skeleton. This initial class can then be used to deploy a local METOC service.

2.1.1

Generating Skeletons

The same tools used to generate stubs for connecting to web services often provide a means of generating server-side code to implement a service. These skeletons have unimplemented methods that can be modified to provide a full JMBL implementation for data providers. For example, the options in the previous example to generate stub classes can be modified to include ss, -ssi, and g. These options will generate both client and server classes along with all related configurations needed to deploy the service. For simple data consumers or clients, generating skeleton classes is not needed. However, without direct access to an NMDSF server or for local performance testing, creating a new JMBL service may have its advantages. For Navy sites, an NMDSF installation is required. With a local installation, it is possible to integrate new data sources using APIs with little or no web services knowledge. An NMDSF deployment is complex; numerous business rules are involved in creating and providing properly formatted JMBL responses. Generating skeleton classes affords sites the ability to create a prototype system to simulate the request/response paradigm. A full discussion of this topic is not included in this guide, but has been used by customers of NMDSF for simulation and testing.

Query by Spatial Vector


Point data from NMDSF is retrieved by value (for the response containment portion of the request). With this retrieval method, the data itself is embedded in the response. This contrasts with the by URI response containment option, which requires a subsequent request to download the data (discussed in the next section). The following example uses stub classes generated from the previous section. The purpose of this example is to demonstrate the ability to perform arbitrary point queries (provided a latitude and longitude) and receive data in the response. Example usage is to simply run this as a Java application as in:
java PointDataRetriever [process] [latitude] [longitude]

The command line arguments are optional, the default is to use the spatial point (latitude, longitude) = (40, -40).
import import import import import import import import import import import java.rmi.RemoteException; java.math.BigDecimal; java.util.Calendar; java.util.List; java.util.ArrayList; java.util.TimeZone; java.text.DateFormat; java.text.SimpleDateFormat; java.text.ParseException; org.apache.axis2.AxisFault; wsdl.jmbl.metoc.JMBLWebServiceStub;

19

import import import import import import import import import import import import import import import import import import import import import import import import import import

jmbl.jmcbl.metoc.ResponseListDocument; jmbl.jmcbl.metoc.RequestListDocument; jmbl.jmcbl.metoc.DataItemDocument.DataItem; jmbl.jmcbl.metoc.DiscreteGeometricDocument.DiscreteGeometric; jmbl.jmcbl.metoc.ErrorConditionDocument.ErrorCondition; jmbl.jmcbl.metoc.ErrorConditionValueAttribute.ErrorConditionValue; jmbl.jmcbl.metoc.ForecastTimeDocument.ForecastTime; jmbl.jmcbl.metoc.GridParameterDocument.GridParameter; jmbl.jmcbl.metoc.GriddedDataDocument.GriddedData; jmbl.jmcbl.metoc.InformationTypeDocument.InformationType; jmbl.jmcbl.metoc.LocationDocument.Location; jmbl.jmcbl.metoc.METOCdataDocument.METOCdata; jmbl.jmcbl.metoc.MetocDataTypeDocument.MetocDataType; jmbl.jmcbl.metoc.ParameterDocument.Parameter; jmbl.jmcbl.metoc.ParameterUnitAttribute.ParameterUnit; jmbl.jmcbl.metoc.RequestDocument.Request; jmbl.jmcbl.metoc.RequestFormatDocument.RequestFormat; jmbl.jmcbl.metoc.RequestListDocument.RequestList; jmbl.jmcbl.metoc.ResponseListDocument.ResponseList; jmbl.jmcbl.metoc.ResponseDocument.Response; jmbl.jmcbl.metoc.ResponseStatusDocument.ResponseStatus; jmbl.jmcbl.metoc.SpatialPointDocument.SpatialPoint; jmbl.jmcbl.metoc.SpatialVectorDocument.SpatialVector; jmbl.jmcbl.metoc.TimeDocument.Time; jmbl.jmcbl.metoc.ValueDocument.Value; jmbl.jmcbl.metoc.ResponseContainmentAttribute.ResponseContainment;

public class PointDataRetriever { public static final String URL = "http://localhost:8080/navows/services/JMBLWebService"; public static final String LATITUDE = "40"; public static final String LONGITUDE = "-40"; public static void main(String args[]) { try { PointDataRetriever retriever = new PointDataRetriever(); String processName = null; String latitude = LATITUDE; String longitude = LONGITUDE; if (args != null && args.length > 0) { processName = args[0]; if (args.length > 2) { latitude = args[1]; longitude = args[2]; } } JMBLWebServiceStub stub = new JMBLWebServiceStub(URL); retriever.query(stub, processName, latitude, longitude); } catch (Throwable t) { t.printStackTrace(System.err); } } public void query(JMBLWebServiceStub stub, String processName, String latitude, String longitude) throws AxisFault, RemoteException { /* setup the request */ RequestListDocument requestListDocument = RequestListDocument.Factory.newInstance(); RequestList requestList = requestListDocument.addNewRequestList(); Request request = requestList.addNewRequest(); InformationType informationType = request.addNewInformationType(); MetocDataType metocDataType = informationType.addNewMetocDataType(); GriddedData griddedData = metocDataType.addNewGriddedData(); addBasicInfo(griddedData, processName); addLocation(griddedData, latitude, longitude); addForecastTime(griddedData); addParameters(griddedData); addRequestFormat(request); /* perform the query and obtain the response list */ ResponseListDocument responseList = stub.getMETOCRequest( requestListDocument); displayMETOCData(responseList.getResponseList());

20

} private void addBasicInfo(GriddedData griddedData, String processName) { griddedData.addNewGriddedAnalysisForecast(); if (processName != null) { griddedData.addNewProcess().setProcessName(processName); } } private void addLocation(GriddedData griddedData, String latitude, String longitude) { Location location = griddedData.addNewLocation(); DiscreteGeometric discreteGeo = location.addNewDiscreteGeometric(); SpatialVector vector = discreteGeo.addNewSpatialVector(); SpatialPoint point = vector.addNewSpatialPoint(); point.setLatitude(new BigDecimal(latitude)); point.setLongitude(new BigDecimal(longitude)); } private void addRequestFormat(Request request) { RequestFormat requestFormat = request.addNewRequestFormat(); requestFormat.setResponseContainment(ResponseContainment.BY_VALUE); } private void addForecastTime(GriddedData griddedData) { DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); Time time = griddedData.addNewTime(); ForecastTime forecastTime = time.addNewForecastTime(); List<String> periods = new ArrayList<String>(); periods.add("0"); periods.add("6"); forecastTime.setForecastPeriod(periods); Calendar calendar = Calendar.getInstance(); try { calendar.setTime(formatter.parse("2007-12-13 00:00:00")); } catch (ParseException e) { e.printStackTrace(); } forecastTime.setBaseReferenceTime(calendar); } private void addParameters(GriddedData griddedData) { GridParameter gridParameter = griddedData.addNewGridParameter(); Parameter parameter = gridParameter.addNewParameter(); parameter.setParameterName("waveHeightSignificant"); parameter.setParameterUnit(ParameterUnit.METERS); gridParameter = griddedData.addNewGridParameter(); parameter = gridParameter.addNewParameter(); parameter.setParameterName("waveSwellDirection"); parameter.setParameterUnit(ParameterUnit.DEGREES); } private int displayStatus(ResponseStatus responseStatus) { if (responseStatus == null) return 0; ErrorCondition errors[] = responseStatus.getErrorConditionArray(); ErrorConditionValue.Enum value = null; for (int j = 0; j < errors.length; j++) { value = errors[j].getErrorConditionValue(); switch (value.intValue()) { case ErrorConditionValue.INT_DATABASE_UNAVAILABLE: System.out.print("Database unavailable"); break; case ErrorConditionValue.INT_ERROR_IN_REQUEST_STRUCTURE: System.out.print("Error in structure"); break; case ErrorConditionValue.INT_GENERIC_ERROR: System.out.print("Generic error"); break; case ErrorConditionValue.INT_PARAMETER_NOT_SUPPORTED: System.out.print("Parameter not supported"); break;

21

default: System.out.print("Unknown error"); break; } if (errors[j].getErrorDescription() != null) System.out.println(": " + errors[j].getErrorDescription()); else System.out.println(); } } return errors.length;

private void displayMETOCData(ResponseList responseList) { Response responses[] = responseList.getResponseArray(); DataItem dataItems[] = null; for (int i = 0; i < responses.length; i++) { dataItems = responses[i].getDataItemArray(); if (displayStatus(responses[i].getResponseStatus()) > 0) continue; for (int j = 0; j < dataItems.length; j++) { displayMETOCData(dataItems[j].getMETOCdataArray()); } } } private void displayMETOCData(METOCdata array[]) { if (array == null) return; for (int k = 0; k < array.length; k++) { displayMETOCData(array[k]); } } private void displayMETOCData(METOCdata metocData) { System.out.print(metocData.getDataElementName()); System.out.print(": "); Value values[] = metocData.getValueArray(); for (int l = 0; l < values.length; l++) { if (values[l].isSetDoubleValue1()) { System.out.print(values[l].getDoubleValue1()); } else if (values[l].isSetFloatValue1()) { System.out.print(values[l].getFloatValue1()); } else if (values[l].isSetIntegerValue()) { System.out.print(values[l].getIntegerValue()); } else if (values[l].isSetLongValue1()) { System.out.print(values[l].getLongValue1()); } else if (values[l].isSetStringValue1()) { System.out.print(values[l].getStringValue1()); } } if (metocData.getParameterUnit() != null) System.out.println(" " + metocData.getParameterUnit()); else System.out.println(); displayMETOCData(metocData.getMETOCdataArray()); } }

Query by Bounding Box


Area data from NMDSF is retrieved by reference (for the response containment portion of the request). With this retrieval method, the data itself is available as a secondary download whose address is encoded within the initial response. The following example uses stub classes generated from the previous section. The purpose of this example is to demonstrate the ability to perform arbitrary area queries (provided the upper right and lower left latitude and longitude points). This example is run as a Java application like:
java AreaDataRetriever [process]

22

In this example below, the parameter specified is bottomDepth. The downloaded data is stored in the current working directory based on the response and data item offset numbers within the returned array.
import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import java.rmi.RemoteException; java.net.MalformedURLException; java.net.URL; java.math.BigDecimal; java.io.OutputStream; java.io.InputStream; java.io.BufferedInputStream; java.io.BufferedOutputStream; java.io.FileOutputStream; java.io.IOException; org.apache.axis2.AxisFault; wsdl.jmbl.metoc.JMBLWebServiceStub; jmbl.jmcbl.metoc.ResponseListDocument; jmbl.jmcbl.metoc.RequestListDocument; jmbl.jmcbl.metoc.DataItemDocument.DataItem; jmbl.jmcbl.metoc.ErrorConditionDocument.ErrorCondition; jmbl.jmcbl.metoc.ErrorConditionValueAttribute.ErrorConditionValue; jmbl.jmcbl.metoc.FormatDocument.Format; jmbl.jmcbl.metoc.GridParameterDocument.GridParameter; jmbl.jmcbl.metoc.GriddedDataDocument.GriddedData; jmbl.jmcbl.metoc.InformationTypeDocument.InformationType; jmbl.jmcbl.metoc.LocationDocument.Location; jmbl.jmcbl.metoc.MetocDataTypeDocument.MetocDataType; jmbl.jmcbl.metoc.ParameterDocument.Parameter; jmbl.jmcbl.metoc.ParameterUnitAttribute.ParameterUnit; jmbl.jmcbl.metoc.RequestDocument.Request; jmbl.jmcbl.metoc.RequestFormatDocument.RequestFormat; jmbl.jmcbl.metoc.RequestListDocument.RequestList; jmbl.jmcbl.metoc.ResponseListDocument.ResponseList; jmbl.jmcbl.metoc.ResponseDocument.Response; jmbl.jmcbl.metoc.ResponseStatusDocument.ResponseStatus; jmbl.jmcbl.metoc.BoundingBoxDocument.BoundingBox; jmbl.jmcbl.metoc.ResponseContainmentAttribute.ResponseContainment;

public class AreaDataRetriever { public static final String URL = "http://localhost:8080/navows/services/JMBLWebService"; public static final String UPPER_RIGHT_LATITUDE = "35"; public static final String UPPER_RIGHT_LONGITUDE = "0"; public static final String LOWER_LEFT_LATITUDE = "30"; public static final String LOWER_LEFT_LONGITUDE = "-10"; public static void main(String args[]) { try { AreaDataRetriever retriever = new AreaDataRetriever(); String processName = null; if (args != null && args.length > 0) { processName = args[0]; } JMBLWebServiceStub stub = new JMBLWebServiceStub(URL); retriever.query(stub, processName); } catch (Throwable t) { t.printStackTrace(System.err); }

public void query(JMBLWebServiceStub stub, String processName) throws AxisFault, RemoteException { /* setup the request */ RequestListDocument requestListDocument = RequestListDocument.Factory.newInstance(); RequestList requestList = requestListDocument.addNewRequestList(); Request request = requestList.addNewRequest(); InformationType informationType = request.addNewInformationType(); MetocDataType metocDataType = informationType.addNewMetocDataType(); GriddedData griddedData = metocDataType.addNewGriddedData(); addBasicInfo(griddedData, processName); addLocation(griddedData);

23

addParameters(griddedData); addRequestFormat(request); /* perform the query and obtain the response list */ ResponseListDocument responseList = stub.getMETOCRequest( requestListDocument); displayResponses(responseList.getResponseList());

private void addBasicInfo(GriddedData griddedData, String processName) { griddedData.addNewGriddedClimatology(); if (processName != null) { griddedData.addNewProcess().setProcessName(processName); }

private void addLocation(GriddedData griddedData) { Location location = griddedData.addNewLocation(); BoundingBox boundingBox = location.addNewBoundingBox(); boundingBox.setLowerLeftLatitude( new BigDecimal(LOWER_LEFT_LATITUDE)); boundingBox.setLowerLeftLongitude( new BigDecimal(LOWER_LEFT_LONGITUDE)); boundingBox.setUpperRightLatitude( new BigDecimal(UPPER_RIGHT_LATITUDE)); boundingBox.setUpperRightLongitude( new BigDecimal(UPPER_RIGHT_LONGITUDE)); } private void addRequestFormat(Request request) { RequestFormat requestFormat = request.addNewRequestFormat(); requestFormat.setResponseContainment(ResponseContainment.BY_REFERENCE); } private void addParameters(GriddedData griddedData) { GridParameter gridParameter = griddedData.addNewGridParameter(); Parameter parameter = gridParameter.addNewParameter(); parameter.setParameterName("depthBottom"); parameter.setParameterUnit(ParameterUnit.METERS); } private int displayStatus(ResponseStatus responseStatus) { if (responseStatus == null) return 0; ErrorCondition errors[] = responseStatus.getErrorConditionArray(); ErrorConditionValue.Enum value = null; for (int j = 0; j < errors.length; j++) { value = errors[j].getErrorConditionValue(); switch (value.intValue()) { case ErrorConditionValue.INT_DATABASE_UNAVAILABLE: System.out.print("Database unavailable"); break; case ErrorConditionValue.INT_ERROR_IN_REQUEST_STRUCTURE: System.out.print("Error in structure"); break; case ErrorConditionValue.INT_GENERIC_ERROR: System.out.print("Generic error"); break; case ErrorConditionValue.INT_PARAMETER_NOT_SUPPORTED: System.out.print("Parameter not supported"); break; default: System.out.print("Unknown error"); break; } if (errors[j].getErrorDescription() != null) System.out.println(": " + errors[j].getErrorDescription()); else System.out.println(); } return errors.length; }

24

private void displayResponses(ResponseList responseList) { Response responses[] = responseList.getResponseArray(); DataItem dataItems[] = null; Format format = null; for (int i = 0; i < responses.length; i++) { dataItems = responses[i].getDataItemArray(); if (displayStatus(responses[i].getResponseStatus()) > 0) continue; for (int j = 0; j < dataItems.length; j++) { format = dataItems[j].getFormat(); if (format == null) continue; try { downloadData("file-R" + i + "-D" + j + ".nc", format.getNetworkAddressName()); } catch (Throwable t) { t.printStackTrace(System.err); } } } } private void downloadData(String filename, String networkAddressName) throws MalformedURLException, IOException { URL url = new URL(networkAddressName); byte data[] = new byte[8092]; OutputStream ostream = null; InputStream istream = null; try { ostream = new BufferedOutputStream(new FileOutputStream(filename)); istream = new BufferedInputStream(url.openStream()); int length = 0; while ((length = istream.read(data)) > 0) { ostream.write(data, 0, length); } System.out.println("Saved data to " + filename); } finally { if (ostream != null) { try { ostream.close(); } catch (Exception e) { /* ignore */ } } if (istream != null) { try { istream.close(); } catch (Exception e) { /* ignore */ } } } } }

Castor
This section is dedicated to discussing Castor XML, the tool used within NMDSF for serializing and deserializing the SOAP payload in a JMBL request and response. As mentioned previously, a number of other tools are available within Apache Axis for data binding. One option, with only cursory mention in the Axis documentation, is to use Castor. There are minimal implications to consider for deployment (i.e., no requirement for binary xsb files as in XMLBeans) and the ability exists to perform schema validation. Castor is also highly flexible and provides a convenient (e.g., without Factories) object interface. However, the unfortunately side effect of flexibility is increased complexity. This section is devoted to those projects that desire using Castor for data binding. This discussion of Castor in this section uses version 1.1.1. Although the full version can be obtained from the Castor web site, only the Castor-XML distribution is needed for web services (the full version includes additional Relational Database support).

25

JMBL Class Extraction


To create Java Classes from the JMBL, the source file generator within Castor must be run. The example ant target below should be sufficient to get started on this task. This example snippet should be part of the larger project build script (e.g., build.xml) file at the root project directory (basedir). The source files generated are located in the src directory and are created within the jmcbl.metoc package. The generated classes from this step are used within the other examples in this section.
<target name="extract" description="Extract java classes from XSD's"> <echo message=removing old generated classes... /> <delete dir=./src/jmcbl/metoc /> <echo message="generating java classes from schemas..." /> <java fork="true" classname="org.exolab.castor.builder.SourceGeneratorMain" jvmversion="5.0" dir="${basedir}"> <classpath refid="project.classpath" /> <arg value="-i" /> <arg file="schemas/combined.xsd"/> <arg value="-generateImportedSchemas" /> <arg value="-package" /> <arg value="jmcbl.metoc" /> <arg value="-binding-file" /> <arg file="${basedir}/bindings.xml" /> <arg value="-dest" /> <arg file="./src/" /> <arg value="[-type-factory java.util.Collection" /> <arg value="-verbose" /> </java> </target>

The project.classpath is defined to include all relevant Castor XML jar files: castor-1.1.1-codegen.jar, castor-1.1.1-xml.jar, commons-logging-1.1.jar, wstx-asl-3.2.1.jar, xercesImpl-2.8.1.jar, and xml-apis1.3.03.jar. In order to customize the behavior of class generation, a binding XML file must be used. Two good reasons to use such files include resolving naming collisions or adding validation rules. Thus, the following bindings.xml file (reference the Castor documentation9) is located at the root project directory:
<?xml version="1.0" encoding="UTF-8"?> <binding xmlns="http://www.castor.org/SourceGenerator/Binding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.castor.org/SourceGenerator/Binding binding.xsd" defaultBindingType="element"> <package> <name>jmcbl.metoc</name> <namespace>urn:metoc:jmcbl:jmbl</namespace> </package> <package> <name>jmcbl.metoc.types</name> <namespace>urn:types:metoc:jmcbl</namespace> </package> <!-Force the class name of the type to whatever its parent Element expects. Problem seems to lay with <Elements> with a certain subset of Attributes. These are Attributes that are 'restrictions' over 'Enumerations' of xsd:String

Note the use of X-Path in the name for each binding, as: /PARENT-ELEMENT/@attribute --> <attributeBinding name="/AltitudePoint/@altitudeUnits"> <java-class name="AltitudeUnitsType" /> </attributeBinding> <attributeBinding name="/DataItemErrorCondition/@dataItemErrorConditionValue">

Castor XML Code Generator Bindings, http://castor.codehaus.org/srcgen-binding.html.

26

<java-class name="DataItemErrorConditionValueType" /> </attributeBinding> <attributeBinding name="/DataItemStatus/@dataItemOrderStatus"> <java-class name="DataItemOrderStatusType"/> </attributeBinding> <attributeBinding name="/DepthPoint/@depthUnits"> <java-class name="DepthUnitsType"/> </attributeBinding> <attributeBinding name="/ErrorCondition/@errorConditionValue"> <java-class name="ErrorConditionValueType" /> </attributeBinding> <attributeBinding name="/JMdeclassificationExemption/@declassificationExemption"> <java-class name="DeclassificationExemptionType" /> </attributeBinding> <attributeBinding name="/Parameter/@parameterUnit"> <java-class name="ParameterUnitType" /> </attributeBinding> <attributeBinding name="/RequestFormat/@distributionMethod"> <java-class name="DistributionMethodType" /> </attributeBinding> <attributeBinding name="/RequestFormat/@responseContainment"> <java-class name="ResponseContainmentType" /> </attributeBinding> <attributeBinding name="/ResponseStatus/@orderStatus"> <java-class name="OrderStatusType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@classification"> <java-class name="ClassificationType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@distributionRestriction"> <java-class name="DistributionRestrictionType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@securityInformationType"> <java-class name="SecurityInformationTypeType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@classificationReason"> <java-class name="ClassificationReasonType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@securityHandlingRestriction"> <java-class name="SecurityHandlingRestrictionType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@distributionReason"> <java-class name="DistributionReasonType" /> </attributeBinding> <attributeBinding name="/SecurityMarking/@distributionReason"> <java-class name="DistributionReasonType" /> </attributeBinding> <attributeBinding name="/SpatialIncrement/@distanceIncrementUnits"> <java-class name="DistanceIncrementUnitsType" /> </attributeBinding> <attributeBinding name="/Theater/@resolutionUnits"> <java-class name="ResolutionUnitsType" /> </attributeBinding> <attributeBinding name="/TimeSeries/@timeIncrementUnits"> <java-class name="TimeIncrementUnitsType" /> </attributeBinding> <attributeBinding name="/VerticalDimension/@verticalDimensionUnits"> <java-class name="VerticalDimensionUnitsType" /> </attributeBinding> </binding>

The bindings file allows Castor to perform customizations from the default code generation semantics. Additional options are also available via a run-time configuration discussed in the next section. Most other tools used for Axis do not provide a Castor-like ability for customizing the class file generation behavior.

Run-time Configuration
In NMDSF, a custom DateFieldHandler was created to support the ability to serialize and deserialize dates in UTC. This change was submitted to the Castor Development Team and should be supported in later releases. Many configuration options can be overridden at run-time using the castor.properties file 27

(located in the root CLASSPATH directory). One such example is to override the time zone using the example below (the default is to use the system time zone for serialization):
# Configures the default time zone to apply to dates/times fetched from # database fields (if not already part of the data). Specify same format as # in java.util.TimeZone.getTimeZone, or the empty string to use the computer's # local time zone. # org.exolab.castor.jdo.defaultTimeZone=UTC

Bean Descriptor Cache


Castor utilizes a set of descriptor classes, one for each class it marshalls and demarshalls. To enforce the rules of the schema (i.e., what child objects must a particular bean contain, in what order, multiplicity, or values an element or attribute have), the descriptors are created when the JMBL beans are generated. Each time a bean is transferred (XML-to-Object and Object-to-XML) Castor loads all the descriptors. There is a significant amount of overhead in loading this information. To reduce this time, these descriptors can be loaded into memory one time and stored in a cache. Castor is then configured to provide them from this cache. This is shown in greater detail in the next section with additional supporting material in Appendix B. Descriptors can be generated dynamically based solely on the generated classes from a schema (discussed in Section 4.2.1).
package jmcbl.config; import import import import import import org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory; org.castor.mapping.BindingType; org.exolab.castor.xml.ClassDescriptorResolverFactory; org.exolab.castor.xml.ResolverException; org.exolab.castor.xml.XMLClassDescriptorResolver;

public class BeanDescriptorCache { // an injected classloader, if needed. private ClassLoader classLoader = null; // class log private Log logger = LogFactory.getLog(BeanDescriptorCache.class); private XMLClassDescriptorResolver descriptorResolver = null; // this object is a singleton. this is the single, static reference. private static final BeanDescriptorCache beanDesriptorCache = new BeanDescriptorCache(); // private constructor private BeanDescriptorCache() { initialize(); } public static BeanDescriptorCache getInstance() { return beanDesriptorCache; } protected void initialize() { try { // create a descriptor resolver that is used to load the descriptor // files. See the notes in Castor's "best practices" web page. descriptorResolver = (XMLClassDescriptorResolver) ClassDescriptorResolverFactory .createClassDescriptorResolver(BindingType.XML); // use current object context's class loader. ClassLoader classLoader = getClassLoader(); descriptorResolver.setClassLoader(classLoader); descriptorResolver.addPackage("jmcbl.metoc"); descriptorResolver.addPackage("jmcbl.metoc.types"); } catch (ResolverException resolverException) { logger.warn("Error loading JMBL Bean Descriptor Cache! Castor MAY function, albeit slower.", resolverException); }

public XMLClassDescriptorResolver getClassDesriptorResolver() {

28

return descriptorResolver;

protected ClassLoader getClassLoader() { if (null != this.classLoader) { return this.classLoader; } else { // return the current objects' context class loader. // in a web application/j2ee app, we need one from the container // that has correct security permissions.. ("default" one, as here, // should work fine) return this.getClass().getClassLoader(); } } protected void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } }

Serialization and Deserialization


As previously noted in the discussion of Apache Axis, the internal representation of the SOAP requests is done using AXIOM and the Streaming API for XML (StAX10). However, if using Castor, this internal representation must be converted into a Castor representation in order for schema enforcement and Objectto-XML and XML-to-Object conversions to take place. This can be done using the SAXOMBuilder and the StAXSource classes. A complete example is shown in Appendix B (MarshallerDemarshaller). In NMDSF, the org.apache.axis2.receivers.RawXMLINOutMessageReceiver class is configured for the message receiver in the services.xml binding for the service. The MarshallerDemarshaller is then used when performing requests. An example usage is provided below:
/* * Implemention of the IRequestor Interface utilizing Axis 2 client libraries. */ public class Axis2Requestor implements IRequestor { private Map<String, Object> properties = new HashMap<String, Object>(); // Logger private Log logger = LogFactory.getLog(Axis2Requestor.class); // a reference to the singleton marshaller/demarshaller private MarshallerDemarshaller marshallerDemarshaller = MarshallerDemarshaller .getInstance(); /* * public constructor. */ public Axis2Requestor() { } /* * (non-Javadoc) * * @see mil.navy.metoc.jmdsf.IRequestor#getMetoc(java.lang.String, * jmcbl.metoc.RequestList) */ public synchronized ResponseList getMetoc(String serviceUrl, RequestList requestList) throws SystemException, RemoteException { ResponseList responseList = null; try { OMElement requestPayload = marshallerDemarshaller .getRequestListOM(requestList); EndpointReference endPointRef = getEndPointReference(serviceUrl); ServiceClient serviceClient = getServiceClient(endPointRef);

10 JSR 173: Streaming API for XML, http://jcp.org/en/jsr/detail?id=173.

29

// call and WAIT for response (block until data or timeout) OMElement responseOM = serviceClient.sendReceive(requestPayload); // translate back into our JMBL beans responseList = marshallerDemarshaller.getResponseList(responseOM); } catch (ApplicationException appEx) { // rethrow application exceptions as system ones. logger.error(appEx); } } /* * (non-Javadoc) * * @see mil.navy.metoc.jmdsf.IRequestor#setMetoc(java.lang.String, * jmcbl.metoc.ResponseList) */ public synchronized ResponseList setMetoc(String serviceUrl, ResponseList responseList) throws SystemException, RemoteException { ResponseList response = null; try { OMElement responsePayload = marshallerDemarshaller .getResponseListOM(responseList); EndpointReference endPointRef = getEndPointReference(serviceUrl); ServiceClient serviceClient = getServiceClient(endPointRef); // call and WAIT for response (block until data or timeout) OMElement responseOM = serviceClient.sendReceive(responsePayload); // translate back into our JMBL beans responseList = marshallerDemarshaller.getResponseList(responseOM); } catch (ApplicationException appEx) { // rethrow application exceptions as system ones. logger.error(appEx); throw new SystemException(appEx.getLocalizedMessage()); } return response; throw new SystemException(appEx.getLocalizedMessage());

return responseList;

protected ServiceClient getServiceClient(EndpointReference endPointRef) throws AxisFault { // set required options Options options = new Options(); options.setTransportInProtocol(Constants.TRANSPORT_HTTP); // set optional arguments supplied from requestor Iterator iterator = properties.keySet().iterator(); String propertyKey = null; Object value = null; while (iterator.hasNext()) { propertyKey = (String) iterator.next(); value = properties.get(propertyKey); if (value == null) continue; options.setProperty(propertyKey, value); if (logger.isDebugEnabled()) logger.debug("setting connection option " + propertyKey + " to the value " + value); /* XXX: feature of axis 1.2, may not be needed for 1.3 */ if (propertyKey.equalsIgnoreCase(HTTPConstants.SO_TIMEOUT) && value instanceof Integer) { options.setTimeOutInMilliSeconds(((Integer) value).intValue()); }

} options.setTo(endPointRef); // set the service URL

// this is from services file. I also observed Axis 2 "auto parsing"

30

// this value from the document. options.setAction("RequestList"); ServiceClient client = new ServiceClient(); client.setOptions(options); } return client;

protected EndpointReference getEndPointReference(String serviceURL) { return new EndpointReference(serviceURL); } public void setProperties(Map<String, Object> properties) { if (properties == null || properties.size() == 0) this.properties.clear(); else this.properties = properties; }

JMBL Request Templates


It is possible to use an editor or other tools (such as the client discussed in Section 2) to create JMBL request templates. These templates could then be used for requests sent to an NMDSF server. For instance, perhaps there is a reoccurring type of request for data, yet the only piece of information in the JMBL request that changes is the forecast time. A simple approach would be to create a request template and make changes to it for the actual JMBL request submitted to the server. The sample code below illustrates this concept. It accepts a command-line argument representing the base forecast time. It then uses that value by substituting the current time within a request template (template.xml). The results of running this sample code is shown in the request.xml and response.xml files provided. The prerequisites to running this sample code are to include the HttpClient 11and Castor XML libraries in your CLASSPATH (the extensions discussed in previous sections, including the descriptor cache, are not required).
import import import import import import import import import import import import import import import import import import import import import import import import java.io.File; java.io.FileReader; java.io.FileWriter; java.io.IOException; java.io.Reader; java.io.InputStreamReader; java.io.CharArrayReader; java.net.HttpURLConnection; java.text.ParseException; java.text.SimpleDateFormat; java.util.TimeZone; jmcbl.metoc.ForecastTime; jmcbl.metoc.GriddedData; jmcbl.metoc.Request; jmcbl.metoc.RequestList; jmcbl.metoc.ResponseList; org.exolab.castor.xml.Marshaller; org.exolab.castor.xml.Unmarshaller; org.exolab.castor.xml.ValidationException; org.exolab.castor.xml.MarshalException; org.exolab.castor.xml.handlers.DateFieldHandler; org.apache.commons.httpclient.HttpClient; org.apache.commons.httpclient.methods.PostMethod; org.apache.commons.httpclient.methods.InputStreamRequestEntity;

/** * Accepts a command-line argument for a template file. This template is * a fully qualified XML file that conforms to the JMBL schema. The second * argument is the forecast time in the format MM/dd/yyyy. The template is * used and a substitution is done on the forecast time.

11 HTTP Components, http://hc.apache.org/httpclient-3.x/.

31

*/ public class SimpleJMBLSender { public static final String JMBL_URL = "http://localhost:8080/navows/services/JMBLWebService"; public static final String DATE_FORMAT = "MM/dd/yyyy"; private static final String NAMESPACE = "urn:metoc:jmcbl:jmbl"; private static final String NAMESPACE_PREFIX = "jmbl"; public static void main(String args[]) { if (args == null || args.length != 2) { System.err.println("Must supply arg1 as the file template to read"); System.err.println("Must supply arg2 as the forecast date: " + DATE_FORMAT); System.exit(-1); } try { // Force a time zone rather than relying on the JDK DateFieldHandler.setDefaultTimeZone(TimeZone.getTimeZone("UTC")); File file = new File(args[0]); FileReader reader = new FileReader(file); RequestList requestList = readTemplateFile(reader); modifyDateInRequest(requestList, args[1]); ResponseList responseList = submitRequestList(requestList); writeResponseList(responseList); } catch (Exception e) { e.printStackTrace(System.err); System.err.println("Unable to submit request."); System.exit(-1); } } System.exit(0);

private static void modifyDateInRequest(RequestList requestList, String date) throws IllegalArgumentException { Request request = null; GriddedData data = null; SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); int count = 0; // number of requests modified for (int i = 0; i < requestList.getRequestCount(); i++) { request = requestList.getRequest(i); if (request.getInformationType() != null && request.getInformationType().getMetocDataType() != null) { data = request.getInformationType().getMetocDataType() .getGriddedData(); if (data == null || data.getTime() == null) continue; ForecastTime forecastTime = data.getTime().getForecastTime(); try { forecastTime.setBaseReferenceTime(sdf.parse(date)); } catch (ParseException pe) { throw new IllegalArgumentException(pe); } count++; } } System.out.println("Modified " + count + " forecast time(s)");

private static void writeResponseList(ResponseList responseList) throws IOException { FileWriter writer = null; try { writer = new FileWriter("response.xml"); Marshaller.marshal(responseList, writer); } catch (ValidationException ve) { throw new IOException(ve); } catch (MarshalException me) { throw new IOException(me); } finally { if (writer != null)

32

} }

writer.close();

private static throws FileWriter try { String String

void serializeRequestFile(File file, RequestList requestList) IOException { writer = new FileWriter(file); header = getXMLHeader(); footer = getXMLFooter();

writer.write(header); Marshaller marshaller = new Marshaller(writer); marshaller.setValidation(true); marshaller.setNamespaceMapping(NAMESPACE_PREFIX, NAMESPACE); marshaller.setSupressXMLDeclaration(true); marshaller.marshal(requestList); writer.write(footer); } catch (ValidationException ve) { ve.printStackTrace(System.err); throw new IOException(ve); } catch (MarshalException me) { me.printStackTrace(System.err); throw new IOException(me); } finally { if (writer != null) writer.close(); } } private static ResponseList submitRequestList(RequestList requestList) throws IOException { // save the current request list for tracing File file = new File("request.xml"); serializeRequestFile(file, requestList); // initialize the connection PostMethod postMethod = new PostMethod(JMBL_URL); postMethod.setRequestHeader("SOAPAction", "RequestList"); InputStreamRequestEntity input = new InputStreamRequestEntity( new java.io.FileInputStream(file), "text/xml; charset=UTF-8"); postMethod.setRequestHeader("Content-Type", "text/xml; charset=UTF-8"); postMethod.setRequestEntity(input); // make the POST request HttpClient httpClient = new HttpClient(); int code = httpClient.executeMethod(postMethod); if (code != HttpURLConnection.HTTP_OK) throw new IOException( "Invalid response code from SOAP POST method: " + code); return createResponseList(new InputStreamReader( postMethod.getResponseBodyAsStream()));

private static ResponseList createResponseList(Reader input) throws IOException { ResponseList responseList = null; StringBuffer buf = new StringBuffer(128); char cbuf[] = new char[128]; int nchars = 0; while ((nchars = input.read(cbuf)) != -1) buf.append(cbuf, 0, nchars); // Strip out the simple SOAP information int start = buf.indexOf("<soapenv:Body>") + 14; // length of element int end = buf.indexOf("</soapenv:Body>"); String payload = buf.substring(start, end); // Read and convert the data from the string to internal objects CharArrayReader reader = null; try { reader = new CharArrayReader(payload.toCharArray()); responseList = (ResponseList)Unmarshaller.unmarshal( ResponseList.class, reader);

33

} catch (ValidationException ve) { throw new IOException(ve); } catch (MarshalException me) { throw new IOException(me); } finally { if (reader != null) reader.close(); } return responseList; } private static String getXMLHeader() { StringBuffer buf = new StringBuffer(32); buf.append("<?xml version='1.0' encoding='UTF-8'?>"); buf.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"); buf.append("<soapenv:Body>"); return buf.toString(); } private static String getXMLFooter() { StringBuffer buf = new StringBuffer(32); buf.append("</soapenv:Body></soapenv:Envelope>"); return buf.toString(); } private static RequestList readTemplateFile(FileReader reader) throws ValidationException, MarshalException { return (RequestList)Unmarshaller.unmarshal(RequestList.class, reader); } }

For the code above, the request template file below is used to query the Navy Coastal Ocean Model for current, salinity, sea surface height and water temperature. The information highlighted in bold is changed via the second command line argument to SimpleJMBLSender.
<?xml version="1.0" encoding="UTF-8"?> <RequestList xmlns="urn:metoc:jmcbl:jmbl" xmlns:ns1="urn:metoc:jmcbl:jmbl"> <Request ns1:clientRequestId="3D-A"> <InformationType> <MetocDataType> <GriddedData> <GriddedAnalysisForecast /> <Location> <BoundingBox ns1:lowerLeftLatitude="7.0" ns1:lowerLeftLongitude="-172.0" ns1:upperRightLatitude="37.0" ns1:upperRightLongitude="-142.0" /> </Location> <Time> <ForecastTime ns1:baseReferenceTime="2005-01-11T00:00:00.000Z" ns1:forecastPeriod="0" /> </Time> <Process> <ProcessName>NCOM</ProcessName> <Theater ns1:rowResolution="0.125" ns1:rowFinestResolution="0.125" ns1:columnResolution="0.125" ns1:columnFinestResolution="0.125" ns1:resolutionUnits="degrees" /> </Process> <GridParameter> <Parameter ns1:parameterName="currentUComponent" ns1:parameterUnit="metersPerSecond" /> </GridParameter> <GridParameter> <Parameter ns1:parameterName="currentVComponent" ns1:parameterUnit="metersPerSecond" /> </GridParameter> <GridParameter> <Parameter ns1:parameterName="salinityWater"

34

ns1:parameterUnit="practicalSalinityUnits" /> </GridParameter> <GridParameter> <Parameter ns1:parameterName="seaSurfaceHeight" ns1:parameterUnit="meters" /> </GridParameter> <GridParameter> <Parameter ns1:parameterName="temperatureWater" ns1:parameterUnit="degreesCelsius" /> </GridParameter> </GriddedData> </MetocDataType> </InformationType> <RequestFormat ns1:responseContainment="By reference" /> </Request> </RequestList>

Keeping a request template in your application provides you the ability to emit JMBL in any manner you deem fit. In addition, the submission of requests can be decoupled from your main application program which may make future maintenance easier and simplify integration. Finally, this technique also provides flexibility to evolve the templates in any manner fit for your circumstances (e.g., edited directly in an editor such as notepad by experienced personnel). The sample response returned from running the SimpleJMBLSender against an NMDSF server is included below (note the proper forecast time is correctly returned):
<?xml version="1.0" encoding="UTF-8"?> <ResponseList xmlns="urn:metoc:jmcbl:jmbl" xmlns:ns1="urn:metoc:jmcbl:jmbl"> <Response ns1:clientRequestId="3D-A"> <DataItem> <Format ns1:responseContainment="By reference" ns1:distributionFormatCode="NCDF" ns1:networkAddressName="http://localhost:8080/navows/data? requestType=AnalysisForecast&amp;requestName=NCOM&amp;reqdataitem=F4OGT5HB819T" /> <Location> <BoundingBox ns1:lowerLeftLatitude="7" ns1:lowerLeftLongitude="-172" ns1:upperRightLatitude="37" ns1:upperRightLongitude="-142" /> </Location> <Time> <ForecastTime ns1:baseReferenceTime="2007-01-11T00:00:00.000Z" ns1:forecastPeriod="0" /> </Time> <Process ns1:center="230" ns1:subcenter="0"> <ProcessName>NCOM</ProcessName> <Theater ns1:rowResolution="0.125" ns1:rowFinestResolution="0.125" ns1:columnResolution="0.125" ns1:columnFinestResolution="0.125" ns1:resolutionUnits="degrees" /> </Process> <METOCdata ns1:dataElementName="contactOrganization"> <Value> <StringValue>NAVO, N33</StringValue> </Value> </METOCdata> <METOCdata ns1:dataElementName="contactOrganizationInstitution"> <Value> <StringValue>Naval Oceanographic Office</StringValue> </Value> </METOCdata> <METOCdata ns1:dataElementName="currentUComponent" ns1:parameterUnit="metersPerSecond" /> <METOCdata ns1:dataElementName="currentVComponent" ns1:parameterUnit="metersPerSecond" /> <METOCdata ns1:dataElementName="salinityWater" ns1:parameterUnit="practicalSalinityUnits" /> <METOCdata ns1:dataElementName="seaSurfaceHeight" ns1:parameterUnit="meters" /> <METOCdata ns1:dataElementName="temperatureWater" ns1:parameterUnit="degreesCelsius" />

35

<SecurityMarking ns1:classification="U" ns1:distributionRestriction="S" ns1:specialDistribution="Approved for public release. Distribution unlimited." /> </DataItem> <ResponseStatus ns1:orderStatus="Request Filled" /> </Response> </ResponseList>

Obtaining Data by Polling


One of the features available in JMBL is the ability to request data based on when data was last modified. This capability can then be used in an application to continue to poll an NMDSF server for any new data. The easiest manner to demonstrate this is to extend the previous request template example. So rather than requesting a specific forecast time, the example will be modified by creating a base ModifiedSinceTime (JMBL Element) within the template (this replaces ForecastTime in the Time Element):
<Time> <ModifiedSinceTime>2005-01-11T00:00:00.000Z</ModifiedSinceTime> </Time>

Next, the only change to the SimpleJMBLSender source code is to ensure to change this value with the date provided on the command line:
private static void modifyDateInRequest(RequestList requestList, String date) throws IllegalArgumentException { Request request = null; GriddedData data = null; SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); int count = 0; // number of requests modified for (int i = 0; i < requestList.getRequestCount(); i++) { request = requestList.getRequest(i); if (request.getInformationType() != null && request.getInformationType().getMetocDataType() != null) { data = request.getInformationType().getMetocDataType() .getGriddedData(); if (data == null || data.getTime() == null) continue; try { data.getTime().setModifiedSinceTime(sdf.parse(date)); } catch (ParseException pe) { throw new IllegalArgumentException(pe); } count++; } } System.out.println("Modified " + count + " modified since time(s)");

With these two simple changes, the new source code is now able to query a data asset based on when the data was last updated. Both of these examples illustrate changing values in request templates. With an object model available via the Castor class file generation, it is also possible to extend request templates a number of other ways as well (e.g., adding elements, attributes, etc. that conform to the JMBL specification). An associated tool in NMDSF to perform similar polling functions is described in Appendix C. The NMDSF Poller is able to perform queries based on process, a serialized template file, as well as by product.

36

Microsoft .NET
The previous sections illustrated using Java-based tools to generate the needed stubs and perform the bindings for the internal representations of JMBL schema objects. This section briefly discusses the usage of C# and .NET based tools. The material presented in this section uses Visual Studio Express 2008 with .NET 3.5.12 These tools are available for download directly from Microsoft.

Generating Stubs
The tool similar to wsdl2java in Apache Axis for the .NET framework is called wsdl.exe. It is commandline driven (execute the Visual Studio Command Prompt for proper initialization of environment settings) and can be used to create the stub classes needed for local binding. Using this tool is straightforward; run it on the WSDL file in JMBL along with the associated schemas as follows:
wsdl.exe services.wsdl

Useful command-line arguments for this tool are denoted in the table below. This list is not comprehensive; more detailed information can be obtained online13 or using the help option denoted in the table. Option /l[anguage]:language Description Specifies the language to use for the generated proxy class. You can specify CS (C#; default), VB (Visual Basic), JS (JScript) or VJS (Visual J#) as the language argument. Specifies the namespace for the generated proxy or template. The default namespace is the global namespace. Specifies the file (or directory) in which to save the generated proxy code. The tool derives the default file name from the XML Web service name. Generates an abstract class for an XML Web service based on the contracts. The default is to generate client proxy classes. Generates interfaces for server-side implementation of an ASP.NET Web Service. An interface is generated for each binding in the WSDL document(s). The WSDL alone implements the WSDL contract (classes that implement the interface should not include either of the following on class methods: Web Service attributes or Serialization attributes that change the WSDL contract). Displays command syntax and options for the tool.

/n[amespace]:namespace /o[ut]:filename or directoryname /server /serverInterface

/?

At the time this documentation was created, there were several incompatibilities with the version of JMBL employed in NMDSF. The next section discusses the changes needed in order to generate and run the Point Data Retriever Example provided later.
12 Visual Studio Express Editions, http://msdn.microsoft.com/vstudio/express/. 13 Web Services Description Language Tool, http://msdn2.microsoft.com/en-us/library/7h3ystb6(VS.80).aspx

37

Schema Changes
The Joint METOC Board (JMB) strives for compatibility for all platforms. However, the following changes are needed with JMBL 3.27 used in NMDSF. These changes were submitted for inclusion in later versions of JMBL. In the file jmblElements.xsd, locate the section where the GriddedClimatology and GriddedAnalysisForecast elements are defined. Change these to the following definitions:
<xsd:element name="GriddedClimatology"> <xsd:complexType /> </xsd:element> <xsd:element name="GriddedAnalysisForecast"> <xsd:complexType /> </xsd:element>

Next, locate the ConstraintParameters on the Product element in the file jmblRequest.xsd. If there is a maxOccurs attribute, remove it. This foregoes the generation of a multi-dimensional array (not properly recognized by the Web Services Description Language Tool (wsdl.exe). This feature is not currently used in NMDSF; however, without this change, you will receive compiler errors from the generated stub class. In addition, this represents a schema bug from the desired intent (Product has also been deprecated in later versions of JMBL). Alternatively, you may edit the generated class file directly, below is an edited example where typeof(Parameter) is changed to typeof(Parameter []) under ConstraintParameters.
[System.Xml.Serialization.XmlArrayItemAttribute("Parameter", typeof(Parameter []), IsNullable=false)] public Parameter[][] ConstraintParameters { get { return this.constraintParametersField; } set { this.constraintParametersField = value; } }

Query by Spatial Vector


For .NET applications that use Web Services, ensure to add System.Web.Services under the Project References settings (e.g., in the Solution Explorer). The following example illustrates using the generated stub class JMBLWebService generated with the Web Services Description Language Tool (wsdl.exe) using the namespace JMBL. This example uses the C# language to obtain the bottom depth parameter value provided a specific latitude and longitude:
using using using using System; System.Collections.Generic; System.Linq; System.Text;

namespace JMBL { class PointDataRetriever { public const String DEFAULT_PARAMETER = "depthBottom"; public static void Main(string[] args) { try { PointDataRetriever retriever = new PointDataRetriever(); String parameterName = (args != null && args.Length == 1) ? args[0] : null; retriever.execute(ref parameterName); } catch (Exception e) { Console.Write(e); } }

38

private void showOrderStatus(ResponseStatusOrderStatus responseOrderStatus, String error) { switch (responseOrderStatus) { case ResponseStatusOrderStatus.DatabaseUnavailable: Console.Write("Database Unavailable"); break; case ResponseStatusOrderStatus.NoDataAvailable: Console.Write("No Data Available"); break; case ResponseStatusOrderStatus.RequestFailed: Console.Write("Request Failed"); break; case ResponseStatusOrderStatus.RequestFilled: Console.Write("Request Filled"); break; case ResponseStatusOrderStatus.RequestReceived: Console.Write("Request Received"); break; default: Console.Write("Unknown Response"); break; } if (error != null) Console.WriteLine(": " + error); else Console.WriteLine(); } private Request[] createRequestList(ref String parameterName) { Request[] requestList = new Request[1]; Request request = new Request(); requestList[0] = request; request.InformationType = new InformationType(); request.InformationType.MetocDataType = new MetocDataType(); GriddedData griddedData = new GriddedData(); griddedData.Item = new GriddedClimatology(); request.InformationType.MetocDataType.Item = griddedData; griddedData.GridParameter = new GridParameter[1]; griddedData.GridParameter[0] = new GridParameter(); Parameter parameter = new Parameter(); parameter.parameterName = (parameterName == null ? DEFAULT_PARAMETER : parameterName); parameter.parameterUnit = ParameterParameterUnit.meters; parameter.parameterUnitSpecified = true; griddedData.GridParameter[0].Parameter = parameter; DiscreteGeometric discreteGeometric = new DiscreteGeometric(); SpatialVector vector = new SpatialVector(); vector.SpatialPoint = new SpatialPoint[1]; vector.SpatialPoint[0] = new SpatialPoint(); vector.SpatialPoint[0].latitude = 40; vector.SpatialPoint[0].longitude = -40; discreteGeometric.SpatialVector = new SpatialVector[1]; discreteGeometric.SpatialVector[0] = vector; griddedData.Location = new Location(); griddedData.Location.Item = discreteGeometric; } return requestList;

private void showDataItemStatus(DataItemStatus status) { if (status == null) return; switch (status.dataItemOrderStatus) { case DataItemStatusDataItemOrderStatus.DataFilled: Console.WriteLine("Data Filled"); break; case DataItemStatusDataItemOrderStatus.Dataordered: Console.WriteLine("Data Ordered"); break; case DataItemStatusDataItemOrderStatus.DatapartiallyFilled: Console.WriteLine("Data Partially Filled"); break; case DataItemStatusDataItemOrderStatus.NoDataAvailable: Console.WriteLine("No Data Available");

39

} }

break; case DataItemStatusDataItemOrderStatus.RequestNotFilled: Console.WriteLine("Request Not Filled"); break; default: Console.WriteLine("Unknown status"); break;

private void showOrderStatus(ResponseStatus responseStatus, String errorDescription) { showOrderStatus(responseStatus.orderStatus, errorDescription); } private void execute(ref String parameterName) { JMBLWebService service = new JMBLWebService(); Request[] requestList = createRequestList(ref parameterName); String organizationId = null; ResponseStatusOrderStatus responseOrderStatus; String errorDescription = null; bool orderStatus = true; Response[] responses = service.getMETOCRequest(requestList, organizationId, out responseOrderStatus, out orderStatus, out errorDescription); if (orderStatus) showOrderStatus(responseOrderStatus, errorDescription); if (responses != null) { foreach (Response response in responses) { if (response.ResponseStatus != null) showOrderStatus(response.ResponseStatus, errorDescription); if (response.Items == null) continue; Object[] items = response.Items; foreach (DataItem item in items) { showDataItemStatus(item.DataItemStatus); METOCdata[] data = item.METOCdata; foreach (METOCdata idata in data) { Value[] values = idata.Value; foreach (Value value in values) { Console.WriteLine(value.Item + " " + idata.parameterUnit); } } } } } }

} }

40

Appendix A: Joint METOC Broker Language WSDL


The Joint METOC Broker Language WSDL is registered in the Department of Defense XML Registry. The latest version can always be downloaded by registering on this web site. The WSDL is available for public release, and a copy is included below. From an implementation standpoint, you may need to consolidate the XML Schema Definition files into a combined file, as noted below.
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:ns0="urn:metoc:jmcbl:jmbl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:metoc:jmbl:wsdl" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="urn:metoc:jmbl:wsdl"> <types> <!-- for compatibility purposes, it is recommend these XSD files are combined --> <!-- into a single file (e.g., combined.xsd) --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns0="urn:metoc:jmcbl:jmbl" targetNamespace="urn:metoc:jmcbl:jmbl"> <xsd:include schemaLocation="AlphaNumericAtts.xsd"/> <xsd:include schemaLocation="jmblTypes.xsd"/> <xsd:include schemaLocation="jmblAttributes.xsd"/> <xsd:include schemaLocation="jmblElements.xsd"/> <xsd:include schemaLocation="jmblJMOBS.xsd"/> <xsd:include schemaLocation="AlphaNumeric.xsd"/> <xsd:include schemaLocation="jmblRequest.xsd"/> <xsd:include schemaLocation="jmblResponse.xsd"/> </xsd:schema> </types> <message name="getMETOCRequestSoapIn"> <part name="parameters" element="ns0:RequestList"/> </message> <message name="getMETOCRequestSoapOut"> <part name="parameters" element="ns0:ResponseList"/> </message> <portType name="JMBLServiceSoapPortType"> <operation name="getMETOCRequest"> <input message="tns:getMETOCRequestSoapIn"/> <output message="tns:getMETOCRequestSoapOut"/> </operation> </portType> <binding name="JMBLServiceSoapBinding" type="tns:JMBLServiceSoapPortType"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="getMETOCRequest"> <soap:operation soapAction="getMETOCRequest" style="document"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="JMBLWebService"> <port name="JMBLServiceSoap" binding="tns:JMBLServiceSoapBinding"> <!-- this address should be replaced with the production instance URL --> <!-- the localhost address provided below is for illustrative purposes --> <soap:address location="http://localhost:8080/navows/services/JMBLWebService"/> </port> </service> </definitions>

Appendix B: MarshallerDemarshaller Class


This class is used in the transmission of METOC data in order to support Object-to-XML and XML-toObject conversions using Castor. The default Castor installation should also work. The example source code below is primarily for reference for those that seek to enforce UTC, use a bean descriptor cache to improve performance, and provide custom namespace management. This example class below is not required for using Castor directly (e.g., without using Apache Axis as in Section 4.2.5). However, using Axis does have additional benefits when it comes to complex message processing such as security and special routing. As JMBL continues to evolve, it is in your best interest to increase the sophistication of integration as well as decouple changes within the JMBL schemas from your application by using tools and definining application-specific bindings.
package jmcbl.config; import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import java.io.CharArrayReader; java.io.CharArrayWriter; java.io.IOException; java.io.Reader; java.io.InputStream; java.util.Properties; java.util.Enumeration; java.util.TimeZone; javax.xml.stream.XMLInputFactory; javax.xml.stream.XMLOutputFactory; javax.xml.transform.Source; javax.xml.transform.TransformerException; javax.xml.transform.TransformerFactory; javax.xml.transform.sax.SAXResult; javax.xml.transform.stax.StAXSource; org.apache.axiom.om.OMElement; org.apache.axiom.om.impl.builder.SAXOMBuilder; org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory; org.codehaus.stax2.XMLInputFactory2; org.codehaus.stax2.XMLOutputFactory2; org.exolab.castor.util.LocalConfiguration; org.exolab.castor.xml.MarshalException; org.exolab.castor.xml.Marshaller; org.exolab.castor.xml.UnmarshalHandler; org.exolab.castor.xml.Unmarshaller; org.exolab.castor.xml.ValidationException; org.exolab.castor.xml.XMLClassDescriptorResolver; org.exolab.castor.xml.handlers.DateFieldHandler; org.castor.util.ConfigKeys; org.xml.sax.ContentHandler; org.xml.sax.SAXException; jmcbl.metoc.RequestList; jmcbl.metoc.ResponseList; mil.navy.metoc.jmdsf.ApplicationException;

/* * A utility for moving between XML serilizations of RequestList and * ResponseList and those objects, utilizing a Marshaller/Demarshaller * framework - in this case Castor (which the project is bound to). This class * will load additional castor properties from the <pre>jmcbl.config</pre> * package. This allows for a distribution of additional properties to be * loaded in addition to the standard properties per the Castor documentation. * Thus, system properties are loaded, then JAR properties in the distribution, * then the classpath properties and finally the properties from the * CONFIG_FILE named within this class (each successively overriding the * previous definitions as needed. */ public class MarshallerDemarshaller { private static final String NAMESPACE = "urn:metoc:jmcbl:jmbl"; private static final String NAMESPACE_PREFIX = "jmbl"; private static final String CONFIG_FILE = "/jmcbl/config/castor.properties"; private Unmarshaller responseListUnMarshaller = null;

private private private private

Unmarshaller requestListUnMarshaller = null; Log logger = LogFactory.getLog(MarshallerDemarshaller.class); XMLClassDescriptorResolver classDescriptorResolver = null; TransformerFactory transformerFactory = null;

static { // load the codehouse StAX factories (used by AXIOM) XMLInputFactory ipFactory = XMLInputFactory2.newInstance(); ipFactory.setProperty(XMLInputFactory.IS_VALIDATING, true); ipFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true); XMLOutputFactory outputFactory = XMLOutputFactory2.newInstance(); } // singleton object private static final MarshallerDemarshaller marshallerDemarshaller = new MarshallerDemarshaller(); /** * Private Constructor, accesses to this class are controlled via the * getInstance method. */ private MarshallerDemarshaller() { loadProperties(); } /** * access to the singleton instance of this class. */ public static MarshallerDemarshaller getInstance() { return marshallerDemarshaller; } /** * Load the additional properties into our local configuration and setup * the marshaller and unmarshaller. The unmarshallers can be reused. They * can also take advantage of the descriptor cache. Access to them is * synchronized. */ protected void loadProperties() { InputStream istream = null; try { istream = getClass().getResourceAsStream(CONFIG_FILE); Properties properties = null; if (istream != null) { logger.info("Loading custom castor properties in " + CONFIG_FILE); properties = new Properties(); properties.load(istream); LocalConfiguration.getInstance().getProperties().putAll( properties); } else { logger.info("No custom castor properties; missing config " + CONFIG_FILE); } LocalConfiguration localConfig = LocalConfiguration.getInstance(); properties = localConfig.getProperties(); Enumeration keys = properties.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); logger.debug(key + "=" + properties.get(key)); } /* * If we force a timezone, then all dates during wire transfer will * be according to the value specified rather the JDK default. */ String timeZone = properties.getProperty(ConfigKeys.DEFAULT_TIMEZONE); if (timeZone != null) { DateFieldHandler.setDefaultTimeZone(TimeZone .getTimeZone(timeZone)); } } catch (IOException e) { logger.warn("Unable to load properties from " + CONFIG_FILE, e); } finally { if (istream != null) {

} }

try { istream.close(); } catch (IOException e) { logger.warn("Unable to close config file " + CONFIG_FILE, e); }

BeanDescriptorCache descriptorCache = BeanDescriptorCache.getInstance(); classDescriptorResolver = descriptorCache.getClassDesriptorResolver(); responseListUnMarshaller = new Unmarshaller(ResponseList.class); responseListUnMarshaller.setResolver(classDescriptorResolver); requestListUnMarshaller = new Unmarshaller(RequestList.class); requestListUnMarshaller.setResolver(classDescriptorResolver); transformerFactory = TransformerFactory.newInstance();

/** * Static Utility method to serialize a RequestList object into XML via * Castor and it's Descriptor. * * @param requestList A valid RequestList Object * @return XML serialization of the RequestList, as a String */ public synchronized String getRequestListXML(RequestList requestList) throws ApplicationException { String reqListStr = null; CharArrayWriter writer = null; try { writer = new CharArrayWriter(); Marshaller marshaller = new Marshaller(writer); // set the descriptor cache marshaller.setResolver(classDescriptorResolver); marshaller.setValidation(true); marshaller.setNamespaceMapping(NAMESPACE_PREFIX, NAMESPACE); marshaller.setSupressXMLDeclaration(true); // leave out the prolog marshaller.marshal(requestList); reqListStr = writer.toString(); catch (IOException ioEx) { logger.error(ioEx); throw new ApplicationException(ioEx.getLocalizedMessage()); catch (ValidationException validationEx) { logger.error(validationEx); throw new ApplicationException(validationEx.getLocalizedMessage()); catch (MarshalException marshalEx) { logger.error(marshalEx); throw new ApplicationException(marshalEx.getLocalizedMessage()); finally { writer.close(); writer = null;

} } } } } }

return reqListStr;

/** * * @param xmlIn A serialized RequestList XML document fragment. * @return a RequestList */ public synchronized RequestList getRequestList(String xmlIn) throws ApplicationException { RequestList requestList = null; Reader reader = null; try { reader = new CharArrayReader(xmlIn.toCharArray()); requestList = (RequestList)requestListUnMarshaller.unmarshal(reader); } catch (MarshalException marshallEx) { logger.error(marshallEx); throw new ApplicationException(marshallEx.getLocalizedMessage()); } catch (ValidationException validationEx) { logger.error(validationEx);

throw new ApplicationException(validationEx.getLocalizedMessage()); } finally { try { reader.close(); } catch (Exception ex) { logger.debug(ex); } reader = null; } } return requestList;

/** * * @param omElement An Axiom OM Element that is known to be a RequestList * xml document. * @return RequestList JMBL bean object * @throws ApplicationException */ public synchronized RequestList getRequestList(OMElement omElement) throws ApplicationException { RequestList requestList = null; if (null != omElement) { try { Source source = new StAXSource(omElement.getXMLStreamReader()); UnmarshalHandler unmarshalHandler = requestListUnMarshaller.createHandler(); // use cached descriptors. unmarshalHandler.setResolver(classDescriptorResolver); ContentHandler contentHandler = Unmarshaller .getContentHandler(unmarshalHandler); transformerFactory.newTransformer().transform(source, new SAXResult(contentHandler)); requestList = (RequestList) unmarshalHandler.getObject(); } catch (SAXException saxEx) { logger.error(saxEx); throw new ApplicationException(saxEx.getLocalizedMessage()); } catch (TransformerException transformerEx) { logger.error(transformerEx); throw new ApplicationException(transformerEx .getLocalizedMessage()); } } else { throw new ApplicationException( "null OM Element passed into MarshallerDemarshaller for conversion."); } return requestList; } /** * * @param requestList A jmbl bean RequestList object * @return OMElement representation of a requset List (another XML binding) * @throws ApplicationException */ public synchronized OMElement getRequestListOM(RequestList requestList) throws ApplicationException { OMElement omElement = null; if (null != requestList) { try { SAXOMBuilder contentHandler = new SAXOMBuilder(); Marshaller marshaller = new Marshaller(contentHandler); // set the descriptor cache marshaller.setResolver(classDescriptorResolver); marshaller.setValidation(true); marshaller.setMarshalAsDocument(false); // leave out the prolog marshaller.setNamespaceMapping(NAMESPACE_PREFIX, NAMESPACE); marshaller.marshal(requestList); omElement = contentHandler.getRootElement(); } catch (IOException ioEx) {

logger.error(ioEx); throw new ApplicationException(ioEx.getLocalizedMessage()); } catch (ValidationException validationEx) { logger.error(validationEx); throw new ApplicationException(validationEx.getLocalizedMessage()); } catch (MarshalException marshallEx) { logger.error(marshallEx); throw new ApplicationException(marshallEx.getLocalizedMessage()); } } else { throw new ApplicationException( "null RequestList passed for translation into Axiom OM."); } } return omElement;

/** * Static Utility method to serialize a ResponseList object into XML via * Castor and it's Descriptor. * * @param responseList * a valid ResponseList Object * @return XML serialization of the ResponseList, as a String */ public synchronized String getResponseListXML(ResponseList responseList) throws ApplicationException { String respListXML = null; CharArrayWriter writer = null; try { writer = new CharArrayWriter(); Marshaller marshaller = new Marshaller(writer); // set the descriptor cache marshaller.setResolver(classDescriptorResolver); marshaller.setValidation(true); marshaller.setMarshalAsDocument(false); // leave out the prolog marshaller.setNamespaceMapping(NAMESPACE_PREFIX, NAMESPACE); marshaller.marshal(responseList); respListXML = writer.toString(); catch (IOException ioEx) { logger.error(ioEx); throw new ApplicationException(ioEx.getLocalizedMessage()); catch (MarshalException marshallEx) { logger.error(marshallEx); throw new ApplicationException(marshallEx.getLocalizedMessage()); catch (ValidationException validationEx) { logger.error(validationEx); throw new ApplicationException(validationEx.getLocalizedMessage()); finally { writer.close(); writer = null;

} } } } } }

return respListXML;

/** * * @param responseList A JMBL ResponseList object * @return OMElement xml binding object * @throws ApplicationException */ public synchronized OMElement getResponseListOM(ResponseList responseList) throws ApplicationException { OMElement omElement = null; if (null != responseList) { try { SAXOMBuilder contentHandler = new SAXOMBuilder(); Marshaller marshaller = new Marshaller(contentHandler); // set the descriptor cache

marshaller.setResolver(classDescriptorResolver); marshaller.setValidation(true); marshaller.setMarshalAsDocument(false); // leave out the prolog marshaller.setNamespaceMapping(NAMESPACE_PREFIX, NAMESPACE); marshaller.marshal(responseList); omElement = contentHandler.getRootElement(); } catch (IOException ioEx) { logger.error(ioEx); throw new ApplicationException(ioEx.getLocalizedMessage()); } catch (ValidationException validationEx) { logger.error(validationEx); throw new ApplicationException(validationEx .getLocalizedMessage()); } catch (MarshalException marshallEx) { logger.error(marshallEx); throw new ApplicationException(marshallEx.getLocalizedMessage()); } } else { throw new ApplicationException( "null ResponseList passed for translation into Axiom OM."); } } return omElement;

/** * * @param xmlIn A serialized ResponseList XML document fragment * @return a ResponseList */ public synchronized ResponseList getResponseList(String xmlIn) throws ApplicationException { ResponseList responseList = null; Reader reader = null; try { reader = new CharArrayReader(xmlIn.toCharArray()); responseList = (ResponseList)responseListUnMarshaller.unmarshal(reader); } catch (MarshalException marshallEx) { logger.error(marshallEx); throw new ApplicationException(marshallEx.getLocalizedMessage()); } catch (ValidationException validationEx) { logger.error(validationEx); throw new ApplicationException(validationEx.getLocalizedMessage()); } finally { try { reader.close(); } catch (Exception ex) { logger.debug(ex); } reader = null; } return responseList; } /** * * @param omElement An Axiom OM Element that is known to be a ResponseList * xml document. * @return ResponseList JMBL bean object. * @throws ApplicationException */ public synchronized ResponseList getResponseList(OMElement omElement) throws ApplicationException { ResponseList responseList = null; if (null != omElement) { try { Source source = new StAXSource(omElement.getXMLStreamReader()); UnmarshalHandler unmarshalHandler = responseListUnMarshaller.createHandler(); // ensure we use cached descriptors unmarshalHandler.setResolver(classDescriptorResolver);

ContentHandler contentHandler = Unmarshaller .getContentHandler(unmarshalHandler); transformerFactory.newTransformer().transform(source, new SAXResult(contentHandler)); responseList = (ResponseList) unmarshalHandler.getObject(); } catch (SAXException saxEx) { logger.error(saxEx); throw new ApplicationException(saxEx.getLocalizedMessage()); } catch (TransformerException transformerEx) { logger.error(transformerEx); throw new ApplicationException(transformerEx .getLocalizedMessage()); } } else { throw new ApplicationException( "null OM Element passed into MarshallerDemarshaller for conversion."); } } } return responseList;

Appendix C: NMDSF Poller


It is possible to perform routine downloads of METOC data using a tool in NMDSF. The examples in Section 4 provide a start to creating your own tools. The NMDSF Poller is used to make scheduled requests for data and store them (area data requests only) to a local file system. It is configured with a properties file or a serialized JMBL request generated from the client. In the latter case, requests can also be created manually or generated from other tools (any valid JMBL request will work). The Poller is a console-based tool written in Java that uses the JMBL web services Application Programming Interfaces (APIs) to make requests to an NMDSF server. It uses a configurable properties file for extracting the details for making the request and stores returned data files (uniquely named) on the local file system based on the areas configured. It is used three different ways: 1. To process simple requests from the properties file. 2. To process serialized requests (JMBL formatted requests). 3. To process product requests. Each of these methods is now discussed in greater detail in the next few sections. The examples provided assume the installation directory for all scripts and related libraries is c:\poller.

Query by Process
The Process Poller uses settings within the properties file for defining the extents of an area of interest, the parameters of interest, and time filter details. You can also specify which process, or data model, that you want answers obtained from. This is the simplest configuration for usage and requires few configuration settings. There are times, however, when someone may need to make more complex requests. Below is a sample script and configuration that performs a query. If no configuration file is specified, the default is to read the process-poller.properties file located in the current working directory. process-poller.bat: @echo off
set INSTALL_DIR=%cd% set CONF_FILE=c:\poller\process-poller.properties FOR %%c in ("%INSTALL_DIR%\*.jar") DO set POLLER_CLASS_PATH=!POLLER_CLASS_PATH!;%%c java %JAVA_OPTS% -cp %POLLER_CLASS_PATH% %OPTS% mil.navy.metoc.jmdsf.poller.ProcessPoller %CONF_FILE%

process-poller.properties
# Sample properties script for the process poller (serviceUrl should be changed to the real endpoint) navows.poller.serviceUrl=http://localhost:8080/navows/services/JMBLWebService navows.poller.nwLatitude=19 navows.poller.nwLongitude=52 navows.poller.seLatitude=14 navows.poller.seLongitude=56 navows.poller.timeSince=60 navows.poller.dataType=analysisForecast navows.poller.processName=NCOM navows.poller.parameterNames=temperatureWater,salinityWater navows.poller.destDirectory=c:\\poller\\data

Query by Serialized Request


Instead of defining the distinguishing characteristics of your request within the properties file, the configuration file is set with the location of the serialized request file. This is a well-formed JMBL formatted file and in the example below, is named request.xml. Below is a sample script and configuration that performs a query. If no configuration file is specified, the default is to read the serialized-poller.properties file located in the current working directory. serialized-poller.bat @echo off
set INSTALL_DIR=%cd% set CONF_FILE=c:\poller\serialized-poller.properties FOR %%c in ("%INSTALL_DIR%\*.jar") DO set POLLER_CLASS_PATH=!POLLER_CLASS_PATH!;%%c java %JAVA_OPTS% -cp %POLLER_CLASS_PATH% %OPTS% mil.navy.metoc.jmdsf.poller.SerializedPoller %CONF_FILE%

serialized-poller.properties
navows.poller.serviceUrl=http://localhost:8080/navows/services/JMBLWebService navows.poller.requestListPath=c:\\poller\\request.xml navows.poller.timeSince=60 navows.poller.destDirectory=c:\\poller\\data

Query by Product
The Product Poller uses a properties file for configuration as well. However, the details are consistent with asking for products. The current version of NMDSF provides an initial product querying capability. Below is a sample script and configuration that performs a query. If no configuration file is specified, the default is to read the product-poller.properties file located in the current working directory. product-poller.bat: @echo off
set INSTALL_DIR=%cd% set CONF_FILE=c:\poller\product-poller.properties FOR %%c in ("%INSTALL_DIR%\*.jar") DO set POLLER_CLASS_PATH=!POLLER_CLASS_PATH!;%%c java %JAVA_OPTS% -cp %POLLER_CLASS_PATH% %OPTS% mil.navy.metoc.jmdsf.poller.ProductPoller %CONF_FILE%

product-poller.properties
# Sample properties script for the product poller (serviceUrl should be changed to the real endpoint) navows.poller.serviceUrl=http://localhost:8080/navows/services/JMBLWebService navows.poller.nwLatitude=25 navows.poller.nwLongitude=150 navows.poller.seLatitude=25 navows.poller.seLongitude=150 navows.poller.timeSince=60 navows.poller.name=* navows.poller.destDirectory=c:\\poller\\data