Part 1 of this blog gives an overview of an end-to-end example of a geometry-based Web Service. This part dives into some of the interesting details for the implementation of such Web Services. The details are discussed per component that was named in
part 1.
Database schema
This one is pretty straightforward. When defining a table column that needs to store geographical data, you can use the
MDSYS.SDO_GEOMETRY type. For example:
CREATE TABLE RESIDENCE(
RESIDENCE_ID INT NOT NULL,
NAME VARCHAR2(100) NOT NULL,
GEOMETRY SDO_GEOMETRY,
CONSTRAINT PK_RESIDENCE PRIMARY KEY (RESIDENCE_ID)
);
You can use the
SDO_UTIL package to insert GML into
SDO_GEOMETRY types using the
SDO_UTIL.FROM_GMLGEOMETRY and
SDO_UTIL.FROM_GML311GEOMETRY functions.
See the
Oracle Spatial Developer's Guide for more information on the
SDO_GEOMETRY type.
ORM layer
In the ORM layer we map database table rows to POJOs and vice versa using JPA. JPA implementations such as EclipseLink provide out-of-the-box mappings between most common Java data types and database columns. To map more exotic and user-defined Java objects you can use Converters in EclipseLink. You can either use an out-of-the-box converter that is shipped with EclipseLink, or code one yourself by implementing EclipseLink's Converter interface. For more information, see
this blogpost by Doug Clarke.
In this case we need to map the
SDO_GEOMETRY database objects to some sort of Java geometry object. Luckily, EclipseLink ships with an out-of-the-box Converter that maps the
SDO_GEOMETRY type to a
JGeometry object. The
JGeometry class provides all kinds of convenience methods and attributes for working with geographical data. This class is part of the Oracle Spatial Java functionality. It can be used only for Oracle Spatial's SQL type
MDSYS.SDO_GEOMETRY and supports Oracle JDBC Driver version 8.1.7 or higher.
To implement the mapping for geographical data we need to do the following:
- Add the required JARs to the classpath;
- Annotate the JPA entities and attributes.
The
JGeometry class and associated Java classes are contained in the
sdoapi.jar and
sdoutl.jar files. They can be found in the library directory of your Oracle RDBMS installation. Also add the ojdbc JAR to the classpath.
Add a
Convert annotation to the geometry attributes in your JPA entities that need to map to the
SDO_GEOMETRY database types:
@Column
@Convert("JGeometry")
private JGeometry geometry;
Next, add the
StructConverter annotation to the JPA entities containing geometry attributes. The
StructConverter is a specific type of EclipseLink converter that provides out-of-the-box mappings to Oracle RDBMS struct types.
@Entity
@Table(name = "RESIDENCE")
@StructConverter(name = "JGeometry", converter = "org.eclipse.persistence.platform.database.oracle.converters.JGeometryConverter")
public class Residence implements Serializable
The
org.eclipse.persistence.platform.database.oracle.converters.JGeometryConverter provides the actual mapping logic. The name attribute of the StructConverter needs to be same as the attribute value for the
Convert annotation.
Web Service layer
Since GML is an XML format we can use JAXB to generate Java classes for the GML elements that are part of the input and output values of the Web Service operations. There are several ways to generate the JAXB classes including Maven plugins for JAXB or the command-line tool
xjc. A simple example of running xjc is shown by the following command:
xjc -d [target dir of generated classes] [XSD root directory]
In our use case, we had a predefined Web Service interface and used a top-down approach to generate Java classes based on the existing interface. You can use the
wsimport tool to generate the JAX-WS artifacts including the Java WebService class from the WSDL.
Note that in this end-to-end scenario the service is exposed as SOAP Web Service. It is simple to expose the same functionality as a RESTful service. You can use JAX-RS annotations instead of JAX-WS annotations to create a RESTful service that exposes geographical data in GML format. See
the following example that shows how JPA and JAX-RS can be combined to create RESTful services.
Business logic layer
This layer, among others, provides the logic to map between the JAXB generated classes for the GML elements and the
JGeometry objects.
For the conversion from
JGeometry objects to JAXB generated classes for GML elements this involves:
- Use the static methods of the oracle.spatial.util.GML3 class to generate a String containing the textual GML representation of the geographical object;
- Unmarshall the GML String into the JAXB generated classes.
This is shown in the following code snippet:
JAXBContext jaxbContext = JAXBContext.newInstance("net.opengis.gml");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
JAXBElement jaxbGeometry = null;
String gml = GML3.to_GML3Geometry(jGeometry);
ByteArrayInputStream bais = new ByteArrayInputStream(gml.getBytes());
JAXBElement jaxbGeometry = (JAXBElement) unmarshaller.unmarshal(bais);
The GML3 class and supporting code can also be found in the sdoapi.jar and sdoutl.jar files.
For the conversion from JAXB generated classes for GML elements to JGeometry objects you need to retrieve the geographical data from the GML elements and use the static methods of the JGeometry class to instantiate the proper JGeometry object. For example:
JGeometry geometry = JGeometry.createLinearPolygon(coordinates, spatialDimension, spatialReference);
Read about the deployment specifics for this geometry-based Web Service on Oracle WebLogic Server in part 3 of this blog.