Custom Implementation Usage

<< Click to Display Table of Contents >>

Navigation:  Development > Client > Java Library >

Custom Implementation Usage

 

This example shows a more complex implementation of a DTSClient, where we extend the class and customize elements to suit our purposes.

 

First, the Client (CustomClient.java):

import com.alloy.dts.DTSException;
import com.alloy.dts.client.*;
import com.alloy.dts.model.DTSPredicate;
import com.alloy.dts.record.StreamRecord;
import com.alloy.dts.type.DTSTypesClusterManager;
 
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
 
import static com.alloy.dts.type.DTSTypes.*;
 
public class CustomClient extends DTSClient {
 
  // Our client will be a singleton for ease of use throughout the application
  private static CustomClient instance;
  public static CustomClient getInstance() {
      if (instance == null) {
          instance = new CustomClient();
       }
      return instance;
   }
 
  // We will only use one collection, so let's keep it here
  DTSRemoteCollection collection;
 
  public CustomClient() {
      super();
      // we also want to override some root types in our client
      initSpecialTypes();
   }
 
  private void initSpecialTypes() {
      // overriding types is as simple as calling this static method on DTSTypesClusterManager
       // but be aware that you will most likely need to define and implement
       // custom Serialization/Deserialization routines
      DTSTypesClusterManager.overrideRootType(GEOMETRY_TYPE, CustomGeometry.class);
       DTSTypesClusterManager.overrideRootType(POINT_TYPE, CustomGeometry.class);
       DTSTypesClusterManager.overrideRootType(LINE_TYPE, CustomGeometry.class);
       DTSTypesClusterManager.overrideRootType(AREA_TYPE, CustomGeometry.class);
   }
 
  // We override the open() method to automatically wait for our endpoint and set our collection
  @Override
  public void open(String projectName) throws TimeoutException, DTSException{
      super.open(projectName);
       DTSDatasource ds = getDatasource(projectName);
      if (!ds.waitForEndpoints(Arrays.asList("myOnlyConnector"), 60 * 1000)) {
          throw new DTSException("No endpoint", ex);
       }
      collection = ds.getEndpoint("myOnlyConnector").getCollection("public.all_things");
   }
 
  // We decide we want our client to react in some way when a stream times out
  @Override
  public void onClosedStream(String streamId) {
       System.out.println("Stream " + streamId + " has timed out!");
      /*
       do something
        */
  }
 
  // and here is the point of our implementation - a nice shortcut to getting the geometric center of an object
  public double[] getObjectCenter(String objectId) throws Exception {
      // we only need 1 record, but we still need a stream
       // we'll use another way of managing it this time
       // first we get the DTSRemoteRecordStream object using createStream on the collection (which gets fed our query predicate)
      DTSRemoteRecordStream stream = collection.createStream(DTSPredicate.eq("id", objectId));
      // then we advance the stream by 1 so that the record we're looking for makes its way into the buffer
      stream.advance(1);
      // then we grab the record from the buffer
      StreamRecord object = (StreamRecord) stream.getCurrentRecordContainer().getRecords().get(0);
      // we use the getFieldValue() method to extract the geometry (which will be in our CustomGeometry format because we overrode the types)
      CustomGeometry geom = (CustomGeometry) object.getFieldValue("geom");
      // and finally call a method we created on CustomGeometry that calculates its center

       // while this particular result could easily be obtained without overriding root types and defining custom deserializers

       // we are using it to illustrate a point and it is easy to see how this can be a powerful tool in more complex scenarios
      return geom.getCenter();
   }
}

 

The CustomGeometry class we're using (CustomGeometry.java):

import com.alloy.dts.DTSException;
import com.alloy.dts.type.json.geojson.GeoJson;
import com.alloy.dts.type.json.geojson.GeoJsonLineString;
import com.alloy.dts.type.json.geojson.GeoJsonPoint;
import com.alloy.dts.type.json.geojson.GeoJsonPolygon;
 
import java.util.ArrayList;
 
public class CustomGeometry {
 
  // We'll hold the GeoJson that comes through
  private GeoJson originalGeom;
 
  public GeoJson getOriginalGeom() {
      return originalGeom;
   }
 
  public void setOriginalGeom(GeoJson originalGeom) {
      this.originalGeom = originalGeom;
   }
 
  // And calculate the center when needed
  public double[] getCenter() throws DTSException {
      if (getOriginalGeom() instanceof GeoJsonPoint) {
          // trivial...
          GeoJsonPoint point = (GeoJsonPoint) getOriginalGeom();
          return point.getCoordinates();
       } else if (getOriginalGeom() instanceof GeoJsonLineString) {
          // naive...
          GeoJsonLineString line = (GeoJsonLineString) getOriginalGeom();
           ArrayList<double[]> coords = line.getCoordinates();
          return new double[] {
                   (coords.get(0)[0] - coords.get(coords.size()-1)[0]) / 2,
                   (coords.get(0)[1] - coords.get(coords.size()-1)[1]) / 2
          };
       } else if (getOriginalGeom() instanceof GeoJsonPolygon) {
          // eh...
          throw new DTSException("Not worth it...");
       }
      throw new DTSException("Unsupported geometry");
   }
}

 

And the custom Deserializer (CustomGeometryDeserializer.java):

// the package is important here! In order for the deserializer to register, it needs to be in this package!

package com.alloy.dts.type.json;
 
import com.mypackage.CustomGeometry;
import com.alloy.dts.type.json.geojson.GeoJson;
import com.google.gson.*;
 
import java.lang.reflect.Type;
 

// Our deserializer needs to implement JsonDeserializer<the_class_we_want_to_output>
public class CustomGeometryDeserializer implements JsonDeserializer<CustomGeometry> {
 
  public static void registerWithGsonBuilder(GsonBuilder builder){
       builder.registerTypeAdapter(CustomGeometry.class, new CustomGeometryDeserializer());
   }
 

  // And here is where we implement our deserialization
  @Override
  public CustomGeometry deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
      // We simply use the deserialization context to deserialize the object to the class it was meant to be
       GeoJson geoJson = context.deserialize(json, GeoJson.class);

      // The we create our custom object and place the original geometry inside it
       CustomGeometry geom = new CustomGeometry();
       geom.setOriginalGeom(geoJson);
 
      return geom;
   }
}