diff --git a/cf/samples/java-samples/commons/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java b/cf/samples/java-samples/commons/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
new file mode 100644
index 0000000..baa56ed
--- /dev/null
+++ b/cf/samples/java-samples/commons/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.typeadapters;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Adapts values whose runtime type may differ from their declaration type. This
+ * is necessary when a field's type is not the same type that GSON should create
+ * when deserializing that field. For example, consider these types:
+ *
{@code
+ * abstract class Shape {
+ * int x;
+ * int y;
+ * }
+ * class Circle extends Shape {
+ * int radius;
+ * }
+ * class Rectangle extends Shape {
+ * int width;
+ * int height;
+ * }
+ * class Diamond extends Shape {
+ * int width;
+ * int height;
+ * }
+ * class Drawing {
+ * Shape bottomShape;
+ * Shape topShape;
+ * }
+ * }
+ * Without additional type information, the serialized JSON is ambiguous. Is
+ * the bottom shape in this drawing a rectangle or a diamond?
{@code
+ * {
+ * "bottomShape": {
+ * "width": 10,
+ * "height": 5,
+ * "x": 0,
+ * "y": 0
+ * },
+ * "topShape": {
+ * "radius": 2,
+ * "x": 4,
+ * "y": 1
+ * }
+ * }}
+ * This class addresses this problem by adding type information to the
+ * serialized JSON and honoring that type information when the JSON is
+ * deserialized: {@code
+ * {
+ * "bottomShape": {
+ * "type": "Diamond",
+ * "width": 10,
+ * "height": 5,
+ * "x": 0,
+ * "y": 0
+ * },
+ * "topShape": {
+ * "type": "Circle",
+ * "radius": 2,
+ * "x": 4,
+ * "y": 1
+ * }
+ * }}
+ * Both the type field name ({@code "type"}) and the type labels ({@code
+ * "Rectangle"}) are configurable.
+ *
+ * Registering Types
+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
+ * name to the {@link #of} factory method. If you don't supply an explicit type
+ * field name, {@code "type"} will be used. {@code
+ * RuntimeTypeAdapterFactory shapeAdapterFactory
+ * = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * }
+ * Next register all of your subtypes. Every subtype must be explicitly
+ * registered. This protects your application from injection attacks. If you
+ * don't supply an explicit type label, the type's simple name will be used.
+ * {@code
+ * shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
+ * shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
+ * shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
+ * }
+ * Finally, register the type adapter factory in your application's GSON builder:
+ * {@code
+ * Gson gson = new GsonBuilder()
+ * .registerTypeAdapterFactory(shapeAdapterFactory)
+ * .create();
+ * }
+ * Like {@code GsonBuilder}, this API supports chaining: {@code
+ * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ * .registerSubtype(Rectangle.class)
+ * .registerSubtype(Circle.class)
+ * .registerSubtype(Diamond.class);
+ * }
+ */
+public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
+ private final Class> baseType;
+ private final String typeFieldName;
+ private final Map> labelToSubtype = new LinkedHashMap>();
+ private final Map, String> subtypeToLabel = new LinkedHashMap, String>();
+ private final boolean maintainType;
+
+ private RuntimeTypeAdapterFactory(Class> baseType, String typeFieldName, boolean maintainType) {
+ if (typeFieldName == null || baseType == null) {
+ throw new NullPointerException();
+ }
+ this.baseType = baseType;
+ this.typeFieldName = typeFieldName;
+ this.maintainType = maintainType;
+ }
+
+ /**
+ * Creates a new runtime type adapter using for {@code baseType} using {@code
+ * typeFieldName} as the type field name. Type field names are case sensitive.
+ * {@code maintainType} flag decide if the type will be stored in pojo or not.
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) {
+ return new RuntimeTypeAdapterFactory(baseType, typeFieldName, maintainType);
+ }
+
+ /**
+ * Creates a new runtime type adapter using for {@code baseType} using {@code
+ * typeFieldName} as the type field name. Type field names are case sensitive.
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) {
+ return new RuntimeTypeAdapterFactory(baseType, typeFieldName, false);
+ }
+
+ /**
+ * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
+ * the type field name.
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType) {
+ return new RuntimeTypeAdapterFactory(baseType, "type", false);
+ }
+
+ /**
+ * Registers {@code type} identified by {@code label}. Labels are case
+ * sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or {@code label}
+ * have already been registered on this type adapter.
+ */
+ public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type, String label) {
+ if (type == null || label == null) {
+ throw new NullPointerException();
+ }
+ if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
+ throw new IllegalArgumentException("types and labels must be unique");
+ }
+ labelToSubtype.put(label, type);
+ subtypeToLabel.put(type, label);
+ return this;
+ }
+
+ /**
+ * Registers {@code type} identified by its {@link Class#getSimpleName simple
+ * name}. Labels are case sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or its simple name
+ * have already been registered on this type adapter.
+ */
+ public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type) {
+ return registerSubtype(type, type.getSimpleName());
+ }
+
+ public TypeAdapter create(Gson gson, TypeToken type) {
+ if (type.getRawType() != baseType) {
+ return null;
+ }
+
+ final Map> labelToDelegate
+ = new LinkedHashMap>();
+ final Map, TypeAdapter>> subtypeToDelegate
+ = new LinkedHashMap, TypeAdapter>>();
+ for (Map.Entry> entry : labelToSubtype.entrySet()) {
+ TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
+ labelToDelegate.put(entry.getKey(), delegate);
+ subtypeToDelegate.put(entry.getValue(), delegate);
+ }
+
+ return new TypeAdapter() {
+ @Override public R read(JsonReader in) throws IOException {
+ JsonElement jsonElement = Streams.parse(in);
+ JsonElement labelJsonElement;
+ if (maintainType) {
+ labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
+ } else {
+ labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
+ }
+
+ if (labelJsonElement == null) {
+ throw new JsonParseException("cannot deserialize " + baseType
+ + " because it does not define a field named " + typeFieldName);
+ }
+ String label = labelJsonElement.getAsString();
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label);
+ if (delegate == null) {
+ throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ + label + "; did you forget to register a subtype?");
+ }
+ return delegate.fromJsonTree(jsonElement);
+ }
+
+ @Override public void write(JsonWriter out, R value) throws IOException {
+ Class> srcType = value.getClass();
+ String label = subtypeToLabel.get(srcType);
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType);
+ if (delegate == null) {
+ throw new JsonParseException("cannot serialize " + srcType.getName()
+ + "; did you forget to register a subtype?");
+ }
+ JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
+
+ if (maintainType) {
+ Streams.write(jsonObject, out);
+ return;
+ }
+
+ JsonObject clone = new JsonObject();
+
+ if (jsonObject.has(typeFieldName)) {
+ throw new JsonParseException("cannot serialize " + srcType.getName()
+ + " because it already defines a field named " + typeFieldName);
+ }
+ //clone.add(typeFieldName, new JsonPrimitive(label));
+
+ for (Map.Entry e : jsonObject.entrySet()) {
+ clone.add(e.getKey(), e.getValue());
+ }
+ Streams.write(clone, out);
+ }
+ }.nullSafe();
+ }
+}
\ No newline at end of file
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloud.java b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloud.java
index 0ab8020..91128a3 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloud.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloud.java
@@ -2,7 +2,7 @@
import java.io.IOException;
-import commons.model.gateway.Measure;
+import commons.model.Measure;
/**
* An abstraction over Cloud Gateways.
@@ -13,7 +13,8 @@ public void connect(String host)
throws IOException;
public void disconnect();
-
+
+ @SuppressWarnings("rawtypes")
public void sendMeasure(Measure measure)
throws IOException;
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudHttp.java b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudHttp.java
index fcb21fa..b6f2c21 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudHttp.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudHttp.java
@@ -10,7 +10,7 @@
import commons.connectivity.HttpClient;
import commons.model.Device;
import commons.model.gateway.Command;
-import commons.model.gateway.Measure;
+import commons.model.Measure;
import commons.utils.Console;
public class GatewayCloudHttp
@@ -50,6 +50,7 @@ public void disconnect() {
httpClient.disconnect();
}
+ @SuppressWarnings("rawtypes")
@Override
public void sendMeasure(Measure measure)
throws IOException {
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudMqtt.java b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudMqtt.java
index 1da401b..78777a4 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudMqtt.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/api/GatewayCloudMqtt.java
@@ -9,7 +9,7 @@
import commons.connectivity.MqttClient;
import commons.connectivity.MqttMessageListener;
import commons.model.Device;
-import commons.model.gateway.Measure;
+import commons.model.Measure;
import commons.utils.Console;
public class GatewayCloudMqtt
@@ -49,6 +49,7 @@ public void disconnect() {
mqttClient.disconnect();
}
+ @SuppressWarnings("rawtypes")
@Override
public void sendMeasure(Measure measure)
throws IOException {
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/connectivity/AbstractClient.java b/cf/samples/java-samples/commons/src/main/java/commons/connectivity/AbstractClient.java
index d48f417..57a5a5f 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/connectivity/AbstractClient.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/connectivity/AbstractClient.java
@@ -3,6 +3,12 @@
import java.io.IOException;
import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;
+
+import commons.model.Measure;
+import commons.model.gateway.JSONMeasure;
+import commons.model.gateway.StringArrayMeasure;
/**
* An abstraction over connectivity clients.
@@ -11,8 +17,16 @@ public abstract class AbstractClient {
protected Gson jsonParser;
+ @SuppressWarnings("rawtypes")
public AbstractClient() {
- jsonParser = new Gson();
+ RuntimeTypeAdapterFactory runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
+ .of(Measure.class)
+ .registerSubtype(StringArrayMeasure.class)
+ .registerSubtype(JSONMeasure.class);
+
+ jsonParser = new GsonBuilder()
+ .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
+ .create();
}
public abstract void connect(String serverUri)
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/model/Measure.java b/cf/samples/java-samples/commons/src/main/java/commons/model/Measure.java
index 6a24af8..05c4097 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/model/Measure.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/model/Measure.java
@@ -1,5 +1,17 @@
package commons.model;
-public class Measure {
+public interface Measure {
+ public String getSensorAlternateId();
+ public String getSensorTypeAlternateId();
+ public String getCapabilityAlternateId();
+ public String getTimestamp();
+ public T getMeasures();
+
+ public void setSensorAlternateId(String sensorAlternateId);
+ public void setSensorTypeAlternateId(String sensorTypeAlternateId);
+ public void setCapabilityAlternateId(String capabilityAlternateId);
+ public void setTimestamp(String timestamp);
+ public void setMeasures(T measures);
+
}
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/JSONMeasure.java b/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/JSONMeasure.java
new file mode 100644
index 0000000..388cb4d
--- /dev/null
+++ b/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/JSONMeasure.java
@@ -0,0 +1,58 @@
+package commons.model.gateway;
+
+import com.google.gson.JsonArray;
+
+public class JSONMeasure implements commons.model.Measure {
+
+private String sensorAlternateId;
+
+ private String sensorTypeAlternateId;
+
+ private String capabilityAlternateId;
+
+ private String timestamp;
+
+ private JsonArray measures;
+
+ public String getSensorAlternateId() {
+ return sensorAlternateId;
+ }
+
+ public void setSensorAlternateId(String sensorAlternateId) {
+ this.sensorAlternateId = sensorAlternateId;
+ }
+
+ public String getSensorTypeAlternateId() {
+ return sensorTypeAlternateId;
+ }
+
+ public void setSensorTypeAlternateId(String sensorTypeAlternateId) {
+ this.sensorAlternateId = sensorTypeAlternateId;
+ }
+
+ public String getCapabilityAlternateId() {
+ return capabilityAlternateId;
+ }
+
+ public void setCapabilityAlternateId(String capabilityAlternateId) {
+ this.capabilityAlternateId = capabilityAlternateId;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+
+ public JsonArray getMeasures() {
+ return measures;
+ }
+
+ public void setMeasures(JsonArray measures) {
+ this.measures = measures;
+ }
+
+}
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/Measure.java b/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/StringArrayMeasure.java
old mode 100644
new mode 100755
similarity index 52%
rename from cf/samples/java-samples/commons/src/main/java/commons/model/gateway/Measure.java
rename to cf/samples/java-samples/commons/src/main/java/commons/model/gateway/StringArrayMeasure.java
index 11580d3..576f1f1
--- a/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/Measure.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/model/gateway/StringArrayMeasure.java
@@ -1,12 +1,32 @@
package commons.model.gateway;
-public class Measure {
+public class StringArrayMeasure implements commons.model.Measure{
+ private String sensorAlternateId;
+
+ private String sensorTypeAlternateId;
+
private String capabilityAlternateId;
+
+ private String timestamp;
+
+ private Object[][] measures;
- private Object[][] measures;
+ public String getSensorAlternateId() {
+ return sensorAlternateId;
+ }
- private String sensorAlternateId;
+ public void setSensorAlternateId(String sensorAlternateId) {
+ this.sensorAlternateId = sensorAlternateId;
+ }
+
+ public String getSensorTypeAlternateId() {
+ return sensorTypeAlternateId;
+ }
+
+ public void setSensorTypeAlternateId(String sensorTypeAlternateId) {
+ this.sensorAlternateId = sensorTypeAlternateId;
+ }
public String getCapabilityAlternateId() {
return capabilityAlternateId;
@@ -16,20 +36,21 @@ public void setCapabilityAlternateId(String capabilityAlternateId) {
this.capabilityAlternateId = capabilityAlternateId;
}
- public Object[][] getMeasures() {
- return measures;
+ public String getTimestamp() {
+ return timestamp;
}
-
- public void setMeasures(Object[][] measures) {
- this.measures = measures;
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
}
- public String getSensorAlternateId() {
- return sensorAlternateId;
+
+ public Object[][] getMeasures() {
+ return measures;
}
- public void setSensorAlternateId(String sensorAlternateId) {
- this.sensorAlternateId = sensorAlternateId;
+ public void setMeasures(Object[][] measures) {
+ this.measures = measures;
}
}
diff --git a/cf/samples/java-samples/commons/src/main/java/commons/utils/EntityFactory.java b/cf/samples/java-samples/commons/src/main/java/commons/utils/EntityFactory.java
index e61d99a..17dea4f 100644
--- a/cf/samples/java-samples/commons/src/main/java/commons/utils/EntityFactory.java
+++ b/cf/samples/java-samples/commons/src/main/java/commons/utils/EntityFactory.java
@@ -6,6 +6,9 @@
import java.util.Random;
import java.util.UUID;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
import commons.model.Capability;
import commons.model.CapabilityType;
import commons.model.Command;
@@ -16,7 +19,8 @@
import commons.model.Sensor;
import commons.model.SensorType;
import commons.model.SensorTypeCapability;
-import commons.model.gateway.Measure;
+import commons.model.gateway.JSONMeasure;
+import commons.model.gateway.StringArrayMeasure;
public class EntityFactory {
@@ -41,8 +45,8 @@ public class EntityFactory {
private static final String TEMPERATURE_PROPERTY_UOM = "°C";
private static final String LIGHT_PROPERTY_UOM = "Lux";
- public static Measure buildAmbientMeasure(Sensor sensor, Capability capability) {
- Measure measure = new Measure();
+ public static StringArrayMeasure buildAmbientMeasure(Sensor sensor, Capability capability) {
+ StringArrayMeasure measure = new StringArrayMeasure();
measure.setCapabilityAlternateId(capability.getAlternateId());
measure.setSensorAlternateId(sensor.getAlternateId());
@@ -51,6 +55,22 @@ public static Measure buildAmbientMeasure(Sensor sensor, Capability capability)
return measure;
}
+
+ public static JSONMeasure buildAmbientMeasure_v2(Sensor sensor, Capability capability) {
+ JSONMeasure measure = new JSONMeasure();
+
+ measure.setCapabilityAlternateId(capability.getAlternateId());
+ measure.setSensorAlternateId(sensor.getAlternateId());
+ JsonArray measureArray = new JsonArray();
+ JsonObject measureObject = new JsonObject();
+ measureObject.addProperty(HUMIDITY_PROPERTY_NAME, buildHumidityPercentage());
+ measureObject.addProperty(TEMPERATURE_PROPERTY_NAME, buildDegreesCelsius());
+ measureObject.addProperty(LIGHT_PROPERTY_NAME, buildLightIlluminance());
+ measureArray.add(measureObject);
+ measure.setMeasures(measureArray);
+
+ return measure;
+ }
public static Command buildSwitchCommand(Sensor sensor, Capability capability) {
Command command = new Command();
diff --git a/cf/samples/java-samples/send-measure/src/main/java/sample/SampleApp.java b/cf/samples/java-samples/send-measure/src/main/java/sample/SampleApp.java
index 1769eae..cd7d371 100644
--- a/cf/samples/java-samples/send-measure/src/main/java/sample/SampleApp.java
+++ b/cf/samples/java-samples/send-measure/src/main/java/sample/SampleApp.java
@@ -20,7 +20,8 @@
import commons.model.GatewayProtocol;
import commons.model.Sensor;
import commons.model.SensorType;
-import commons.model.gateway.Measure;
+import commons.model.gateway.JSONMeasure;
+import commons.model.gateway.StringArrayMeasure;
import commons.utils.Console;
import commons.utils.EntityFactory;
import commons.utils.SecurityUtil;
@@ -113,10 +114,12 @@ private void sendAmbientMeasures(final Sensor sensor, final Capability capabilit
@Override
public void run() {
- Measure measure = EntityFactory.buildAmbientMeasure(sensor, capability);
+ StringArrayMeasure measure = EntityFactory.buildAmbientMeasure(sensor, capability);
+ JSONMeasure measure_v2 = EntityFactory.buildAmbientMeasure_v2(sensor, capability);
try {
gatewayCloud.sendMeasure(measure);
+ gatewayCloud.sendMeasure(measure_v2);
}
catch (IOException e) {
Console.printError(e.getMessage());