/*
* Dog - Device Rest Endpoint
*
* Copyright (c) 2013-2014 Dario Bonino, Luigi De Russis and Teodoro Montanaro
*
* 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 it.polito.elite.dog.communication.rest.device;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicReference;
import javax.measure.Measure;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.device.Constants;
import org.osgi.service.log.LogService;
import it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi;
import it.polito.elite.dog.communication.rest.device.command.ClimateSchedulePayload;
import it.polito.elite.dog.communication.rest.device.command.CommandPayload;
import it.polito.elite.dog.communication.rest.device.command.DailyClimateSchedulePayload;
import it.polito.elite.dog.communication.rest.device.command.DoublePayload;
import it.polito.elite.dog.communication.rest.device.command.ExplicitTeachInPayload;
import it.polito.elite.dog.communication.rest.device.command.HSBColorPayload;
import it.polito.elite.dog.communication.rest.device.command.MeasurePayload;
import it.polito.elite.dog.communication.rest.device.command.RGBColorPayload;
import it.polito.elite.dog.communication.rest.device.status.AllDeviceStatesResponsePayload;
import it.polito.elite.dog.communication.rest.device.status.DeviceStateResponsePayload;
import it.polito.elite.dog.core.devicefactory.api.DeviceFactory;
import it.polito.elite.dog.core.housemodel.api.HouseModel;
import it.polito.elite.dog.core.library.jaxb.Controllables;
import it.polito.elite.dog.core.library.jaxb.Device;
import it.polito.elite.dog.core.library.jaxb.DogHomeConfiguration;
import it.polito.elite.dog.core.library.jaxb.ObjectFactory;
import it.polito.elite.dog.core.library.model.ControllableDevice;
import it.polito.elite.dog.core.library.model.DeviceCostants;
import it.polito.elite.dog.core.library.model.DeviceDescriptor;
import it.polito.elite.dog.core.library.model.DeviceStatus;
import it.polito.elite.dog.core.library.model.devicecategory.Controllable;
import it.polito.elite.dog.core.library.model.state.State;
import it.polito.elite.dog.core.library.model.statevalue.StateValue;
import it.polito.elite.dog.core.library.util.Executor;
import it.polito.elite.dog.core.library.util.LogHelper;
/**
*
* @author <a href="mailto:dario.bonino@polito.it">Dario Bonino</a>
* @author <a href="mailto:luigi.derussis@polito.it">Luigi De Russis</a>
* @see <a href="http://elite.polito.it">http://elite.polito.it</a>
*
*/
@Path("/api/v1/devices/")
public class DeviceRESTEndpoint implements DeviceRESTApi
{
// the service logger
private LogHelper logger;
// the bundle context reference to extract information on the entire Dog
// status
private BundleContext context;
// reference for the HouseModel
private AtomicReference<HouseModel> houseModel;
// reference for the DeviceFactory
private AtomicReference<DeviceFactory> deviceFactory;
// registered payloads
private Vector<Class<? extends CommandPayload<?>>> payloads;
// the instance-level mapper
private ObjectMapper mapper;
// the JAXB context
private JAXBContext jaxbContext;
/**
* Constructor
*/
public DeviceRESTEndpoint()
{
// init the house model atomic reference
this.houseModel = new AtomicReference<HouseModel>();
// init the device factory atomic reference
this.deviceFactory = new AtomicReference<DeviceFactory>();
// init JAXB Context
try
{
this.jaxbContext = JAXBContext.newInstance(DogHomeConfiguration.class.getPackage().getName());
}
catch (JAXBException e)
{
this.logger.log(LogService.LOG_ERROR, "JAXB Init Error", e);
}
// init the set of allowed payloads
this.payloads = new Vector<Class<? extends CommandPayload<?>>>();
this.payloads.add(ClimateSchedulePayload.class);
this.payloads.add(DailyClimateSchedulePayload.class);
// it is really mandatory that double payload precedes measure payload
// to avoid matching pure doubles to measures with no unit.
this.payloads.add(DoublePayload.class);
this.payloads.add(MeasurePayload.class);
this.payloads.add(HSBColorPayload.class);
this.payloads.add(RGBColorPayload.class);
this.payloads.add(ExplicitTeachInPayload.class);
// initialize the instance-wide object mapper
this.mapper = new ObjectMapper();
// set the mapper pretty printing
this.mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
// avoid empty arrays and null values
this.mapper.configure(SerializationConfig.Feature.WRITE_EMPTY_JSON_ARRAYS, false);
this.mapper.setSerializationInclusion(Inclusion.NON_NULL);
// create an introspector for parsing both Jackson and JAXB annotations
AnnotationIntrospector jackson = new JacksonAnnotationIntrospector();
AnnotationIntrospector jaxb = new JaxbAnnotationIntrospector();
AnnotationIntrospector fullIntrospector = new AnnotationIntrospector.Pair(jackson, jaxb);
// make deserializer use both Jackson and JAXB annotations
this.mapper.getDeserializationConfig().withAnnotationIntrospector(fullIntrospector);
// make serializer use both Jackson and JAXB annotations
this.mapper.getSerializationConfig().withAnnotationIntrospector(fullIntrospector);
}
/**
* Bundle activation, stores a reference to the context object passed by the
* framework to get access to system data, e.g., installed bundles, etc.
*
* @param context
*/
public void activate(BundleContext context)
{
// store the bundle context
this.context = context;
// init the logger with a null logger
this.logger = new LogHelper(this.context);
// log the activation
this.logger.log(LogService.LOG_INFO, "Activated....");
}
/**
* Prepare the bundle to be deactivated...
*/
public void deactivate()
{
// null the context
this.context = null;
// log deactivation
this.logger.log(LogService.LOG_INFO, "Deactivated...");
// null the logger
this.logger = null;
}
/**
* Bind the HouseModel service (before the bundle activation)
*
* @param houseModel
* the HouseModel service to add
*/
public void addedHouseModel(HouseModel houseModel)
{
// store a reference to the HouseModel service
this.houseModel.set(houseModel);
}
/**
* Unbind the HouseModel service
*
* @param houseModel
* the HouseModel service to remove
*/
public void removedHouseModel(HouseModel houseModel)
{
this.houseModel.compareAndSet(houseModel, null);
}
/**
* Bind the DeviceFactory service (before the bundle activation)
*
* @param deviceFactory
* the DeviceFactory service to add
*/
public void addedDeviceFactory(DeviceFactory deviceFactory)
{
// store a reference to the HouseModel service
this.deviceFactory.set(deviceFactory);
}
/**
* Unbind the DeviceFactory service
*
* @param deviceFactory
* the DeviceFactory service to remove
*/
public void removedDeviceFactory(DeviceFactory deviceFactory)
{
this.deviceFactory.compareAndSet(deviceFactory, null);
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getAllDevicesInJson()
*/
@Override
public String getAllDevicesInJson()
{
String devicesJSON = "";
// get the JAXB object containing all the configured devices
DogHomeConfiguration dhc = this.getAllDevices();
try
{
devicesJSON = this.mapper.writeValueAsString(dhc.getControllables().get(0));
}
catch (Exception e)
{
this.logger.log(LogService.LOG_ERROR, "Error in creating the JSON representing all the configured devices",
e);
}
// if no devices are available, send a 404 Not found HTTP response
// assume, as before, that only one Controllables tag exists
boolean noDevices = dhc.getControllables().get(0).getDevice().isEmpty();
if (devicesJSON.isEmpty() || noDevices)
{
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else
{
return devicesJSON;
}
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getAllDevicesInXml ()
*/
@Override
public String getAllDevicesInXml()
{
String devicesXML = "";
// get the JAXB object containing all the configured devices
DogHomeConfiguration dhc = this.getAllDevices();
// create the XML for replying the request
devicesXML = this.generateXML(dhc);
// if no devices are available, send a 404 Not found HTTP response
// assume, as before, that only one Controllables tag exists
boolean noDevices = dhc.getControllables().get(0).getDevice().isEmpty();
if (devicesXML.isEmpty() || noDevices)
{
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else
{
return devicesXML;
}
}
/**
* Get all the devices configured in Dog from the {@link HouseModel} in
* their "clean" format, e.g., without all the network-related information
* and unneeded tabs or newlines
*
* @return a {@link DogHomeConfiguration} object with all the devices
* information
*/
private DogHomeConfiguration getAllDevices()
{
// create a JAXB Object Factory for adding the proper header...
ObjectFactory factory = new ObjectFactory();
DogHomeConfiguration dhc = factory.createDogHomeConfiguration();
// check if the HouseModel service is available
if (this.houseModel.get() != null)
{
// get all the devices from the HouseModel
Controllables controllables = this.houseModel.get().getSimpleDevices().get(0);
dhc.getControllables().add(controllables);
}
return dhc;
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getDeviceInJson(java.lang.String)
*/
@Override
public String getDeviceInJson(String deviceId)
{
String deviceJSON = "";
// get the requested device configuration, in JAXB
DogHomeConfiguration dhc = this.getDevice(deviceId);
if (dhc.getControllables().get(0).getDevice() != null)
{
// get the JAXB representation of the desired device
Device requestedDevice = dhc.getControllables().get(0).getDevice().get(0);
try
{
deviceJSON = this.mapper.writeValueAsString(requestedDevice);
}
catch (Exception e)
{
this.logger.log(LogService.LOG_ERROR,
"Error in creating the JSON representing all the configured devices", e);
}
return deviceJSON;
}
else
{
// the requested device is not present, send a 404 Not found HTTP
// response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getDeviceInXml (java.lang.String)
*/
@Override
public String getDeviceInXml(String deviceId)
{
String deviceXML = "";
// get the requested device configuration
DogHomeConfiguration dhc = this.getDevice(deviceId);
if (dhc.getControllables().get(0).getDevice() != null)
{
// create the XML for replying the request
deviceXML = this.generateXML(dhc);
return deviceXML;
}
else
{
// the requested device is not present, send a 404 Not found HTTP
// response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
/**
*
* Get the configuration of the device identified by the parameter deviceId
* from the {@link HouseModel} and perform some "cleaning" operations, such
* as removing all the network-related information and removing unneeded
* tabs or newlines
*
* @param deviceId
* the device unique identifier
* @return a {@link DogHomeConfiguration} object with the required device
* information
*/
private DogHomeConfiguration getDevice(String deviceId)
{
ObjectFactory factory = new ObjectFactory();
DogHomeConfiguration dhc = factory.createDogHomeConfiguration();
// check if the HouseModel service is available
if (this.houseModel.get() != null)
{
// create a JAXB Object Factory for adding the proper header...
Controllables controllables = factory.createControllables();
// get the desired device from the HouseModel service
for (Device device : this.houseModel.get().getSimpleDevices().get(0).getDevice())
{
if (device.getId().equalsIgnoreCase(deviceId))
{
// add the device to its container
controllables.getDevice().add(device);
}
}
dhc.getControllables().add(controllables);
}
return dhc;
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* updateDeviceLocation(java.lang.String, java.lang.String)
*/
@Override
public void updateDeviceLocation(String deviceId, String location)
{
// set and init the variable used to store the HTTP response that will
// be sent by exception to the client
Status response = null;
if (location != null && !location.isEmpty())
{
// create filter for getting the desired device
String deviceFilter = String.format("(&(%s=*)(%s=%s))", Constants.DEVICE_CATEGORY,
DeviceCostants.DEVICEURI, deviceId);
try
{
// try to read the value from the JSON
Device deviceLocation = this.mapper.readValue(location, Device.class);
// get the device service references
ServiceReference<?>[] deviceService = this.context.getAllServiceReferences(
org.osgi.service.device.Device.class.getName(), deviceFilter);
// only one device with the given deviceId can exists in the
// framework...
if (deviceService != null && deviceService.length == 1)
{
// get the OSGi service pointed by the current device
// reference
Object device = this.context.getService(deviceService[0]);
if ((device != null) && (device instanceof ControllableDevice))
{
// get the device instance
ControllableDevice currentDevice = (ControllableDevice) device;
// get the associated device descriptor
DeviceDescriptor currentDeviceDescr = currentDevice.getDeviceDescriptor();
// update the device location, if available
if ((deviceLocation.getIsIn() != null) && (!deviceLocation.getIsIn().isEmpty()))
{
currentDeviceDescr.setLocation(deviceLocation.getIsIn());
// check if the DeviceFactory service is available
if (this.deviceFactory.get() != null)
{
// update the device configuration
this.deviceFactory.get().updateDevice(currentDeviceDescr);
// set the variable used to store the HTTP
// response by the right value
// OK: the device location was successfully
// updated
response = Response.Status.OK;
}
else
{
this.logger
.log(LogService.LOG_WARNING,
"Impossible to update the device location: the Device Factory is not available!");
// set the variable used to store the HTTP
// response by the right value
// PRECONDITION_FAILED: impossible to update the
// device location since the Device Factory is
// not available
// it was the best response status available
response = Response.Status.PRECONDITION_FAILED;
}
}
}
// releases all the services object referenced at the
// beginning of the method
for (ServiceReference<?> singleServiceReference : deviceService)
{
this.context.ungetService(singleServiceReference);
}
}
}
catch (Exception e)
{
this.logger.log(LogService.LOG_ERROR, "Error in updating the location of device " + deviceId, e);
// set the variable used to store the HTTP response by the right
// value
// NOT_MODIFIED: impossible to update the location of the device
response = Response.Status.NOT_MODIFIED;
}
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(response);
}
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* updateDeviceDescription(java.lang.String, java.lang.String)
*/
@Override
public void updateDeviceDescription(String deviceId, String description)
{
// set and init the variable used to store the HTTP response that will
// be sent by exception to the client
Status response = null;
if (description != null && !description.isEmpty())
{
// create filter for getting the desired device
String deviceFilter = String.format("(&(%s=*)(%s=%s))", Constants.DEVICE_CATEGORY,
DeviceCostants.DEVICEURI, deviceId);
try
{
// try to read the value from the JSON
Device deviceDescription = this.mapper.readValue(description, Device.class);
// get the device service references
ServiceReference<?>[] deviceService = this.context.getAllServiceReferences(
org.osgi.service.device.Device.class.getName(), deviceFilter);
// only one device with the given deviceId can exists in the
// framework...
if (deviceService != null && deviceService.length == 1)
{
// get the OSGi service pointed by the current device
// reference
Object device = this.context.getService(deviceService[0]);
if ((device != null) && (device instanceof ControllableDevice))
{
// get the device instance
ControllableDevice currentDevice = (ControllableDevice) device;
// get the associated device descriptor
DeviceDescriptor currentDeviceDescr = currentDevice.getDeviceDescriptor();
// update the device description, if available
if ((deviceDescription.getDescription() != null)
&& (!deviceDescription.getDescription().isEmpty()))
{
currentDeviceDescr.setDescription(deviceDescription.getDescription());
// check if the DeviceFactory service is available
if (this.deviceFactory.get() != null)
{
// update the device configuration
this.deviceFactory.get().updateDevice(currentDeviceDescr);
// set the variable used to store the HTTP
// response by the right value
// OK: the description was successfully updated
response = Response.Status.OK;
}
else
{
this.logger
.log(LogService.LOG_WARNING,
"Impossible to update the device description: the Device Factory is not available!");
// set the variable used to store the HTTP
// response by the right value
// PRECONDITION_FAILED: impossible to update the
// device description since the Device Factory
// is not available
// it was the best response status available
response = Response.Status.PRECONDITION_FAILED;
}
}
}
// releases all the services object referenced at the
// beginning of the method
for (ServiceReference<?> singleServiceReference : deviceService)
{
this.context.ungetService(singleServiceReference);
}
}
}
catch (Exception e)
{
this.logger.log(LogService.LOG_ERROR, "Error in updating the description of device " + deviceId, e);
// set the variable used to store the HTTP response by the right
// value
// NOT_MODIFIED: impossible to update the description of the
// device
// it was the best response status available
response = Response.Status.NOT_MODIFIED;
}
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(response);
}
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getAllDeviceStatus()
*/
public String getAllDeviceStatus()
{
// the response
String responseAsString = "";
boolean listIsEmpty = true;
// get all the installed device services
try
{
// get the device service references
ServiceReference<?>[] allDevices = this.context.getAllServiceReferences(
org.osgi.service.device.Device.class.getName(), null);
// check not null
if (allDevices != null)
{
// create an AllDeviceStatesResponsePayload
AllDeviceStatesResponsePayload responsePayload = new AllDeviceStatesResponsePayload();
// create an array of DeviceStateResponsePayloads
DeviceStateResponsePayload[] deviceStateResponsePayload = new DeviceStateResponsePayload[allDevices.length];
// set the array as part of the response payload
responsePayload.setDevicesStatus(deviceStateResponsePayload);
// iterate over all devices
for (int i = 0; i < allDevices.length; i++)
{
// get the OSGi service pointed by the current device
// reference
Object device = this.context.getService(allDevices[i]);
// check if the service belongs to the set of dog devices
if (device instanceof ControllableDevice)
{
// get the device instance
ControllableDevice currentDevice = (ControllableDevice) device;
// get the response payload for the current device
deviceStateResponsePayload[i] = this.getControllableStatus(currentDevice, allDevices[i]);
// if we are here it means that the list will not be
// empty
listIsEmpty = false;
}
this.context.ungetService(allDevices[i]);
}
// store the device
responsePayload.setDevicesStatus(deviceStateResponsePayload);
// convert the response body to json
responseAsString = this.mapper.writeValueAsString(responsePayload);
// Releases all the services object referenced at the beginning
// of the method
for (ServiceReference<?> singleServiceReference : allDevices)
{
this.context.ungetService(singleServiceReference);
}
}
}
catch (Exception e)
{
this.logger.log(LogService.LOG_ERROR, "Error while composing the response", e);
}
// if the responseAsString variable is empty we have to send an HTTP
// response
// 404 Not found
if (responseAsString.isEmpty() || listIsEmpty == true)
{
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else
return responseAsString;
}
/*
* (non-Javadoc)
*
* @see it.polito.elite.dog.communication.rest.device.api.DeviceRESTApi#
* getDeviceStatus(java.lang.String)
*/
@Override
public String getDeviceStatus(String deviceId)
{
// the response
String responseAsString = "";
boolean listIsEmpty = true;
// create filter for getting the desired device
String deviceFilter = String.format("(&(%s=*)(%s=%s))", Constants.DEVICE_CATEGORY, DeviceCostants.DEVICEURI,
deviceId);
try
{
// get the device service references
ServiceReference<?>[] deviceService = this.context.getAllServiceReferences(
org.osgi.service.device.Device.class.getName(), deviceFilter);
if (deviceService != null)
{
// create a DeviceStateResponsePayload
DeviceStateResponsePayload deviceStateResponsePayload = new DeviceStateResponsePayload();
// only one device with the given deviceId can exists in the
// framework
if (deviceService.length == 1)
{
// get the OSGi service pointed by the current device
// reference
Object device = this.context.getService(deviceService[0]);
if (device instanceof ControllableDevice)
{
// get the device instance
ControllableDevice currentDevice = (ControllableDevice) device;
// get the response payload
deviceStateResponsePayload = this.getControllableStatus(currentDevice, deviceService[0]);
// if we are here it means that the list will not be
// empty
listIsEmpty = false;
}
this.context.ungetService(deviceService[0]);
}
// convert the response body to json
responseAsString = this.mapper.writeValueAsString(deviceStateResponsePayload);
// Releases all the services object referenced at the beginning
// of the method
for (ServiceReference<?> singleServiceReference : deviceService)
{
this.context.ungetService(singleServiceReference);
}
}
}
catch (Exception e)
{
this.logger
.log(LogService.LOG_ERROR, "Error while composing the response for the status of " + deviceId, e);
}
// if the responseAsString variable is empty we have to send an HTTP
// response
// 404 Not found
if (responseAsString.isEmpty() || listIsEmpty == true)
{
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else
return responseAsString;
}
/**
* Build the Jackson representation for the status of a given
* {@link ControllableDevice} object.
*
* @param device
* the {@link ControllableDevice} to query for the status
* @param deviceService
* the OSGi service reference for the given
* {@link ControllableDevice}
* @return a {@link DeviceStateResponsePayload} containing the proper
* response to the status API
*/
private DeviceStateResponsePayload getControllableStatus(ControllableDevice device,
ServiceReference<?> deviceService)
{
// init
DeviceStateResponsePayload deviceStateResponsePayload = null;
// get the device descriptor
DeviceDescriptor deviceDescriptor = device.getDeviceDescriptor();
// create the response payload
deviceStateResponsePayload = new DeviceStateResponsePayload();
// set the device id
deviceStateResponsePayload.setId(deviceDescriptor.getDeviceURI());
// set the activation status of the device
deviceStateResponsePayload
.setActive(Boolean.valueOf((String) deviceService.getProperty(DeviceCostants.ACTIVE)));
// get the device status
Map<String, State> allStates = null;
DeviceStatus state = ((Controllable) device).getState();
if (state != null)
{
allStates = state.getStates();
}
// check if the device state is available, i.e., not null
if (allStates != null)
{
// iterate over all states
for (String stateKey : allStates.keySet())
{
// get the current state
State currentState = allStates.get(stateKey);
// get the values associate to the current state
StateValue currentStateValues[] = currentState.getCurrentStateValue();
// create the response-level state values
Object responseBodyStateValues[] = new Object[currentStateValues.length];
// iterate over the state values
for (int j = 0; j < currentStateValues.length; j++)
{
// get state value features
HashMap<String, Object> features = currentStateValues[j].getFeatures();
// prepare the map to store in the response
// body
HashMap<String, Object> responseBodyFeatures = new HashMap<String, Object>();
// iterate over the features
for (String featureKey : features.keySet())
{
// check the "value" feature and, if it
// is an instance of measure, serialize
// it as a String
if (featureKey.contains("Value"))
{
if (features.get(featureKey) instanceof Measure<?, ?>)
responseBodyFeatures.put("value", features.get(featureKey).toString());
else
responseBodyFeatures.put("value", features.get(featureKey));
}
else
{
Object value = features.get(featureKey);
if ((!(value instanceof String))
|| ((value instanceof String) && (!((String) value).isEmpty())))
responseBodyFeatures.put(featureKey, features.get(featureKey));
}
}
// store the current state value
responseBodyStateValues[j] = responseBodyFeatures;
}
// store the state
deviceStateResponsePayload.getStatus().put(currentState.getClass().getSimpleName(),
responseBodyStateValues);
}
}
return deviceStateResponsePayload;
}
@Override
@GET
@Path("{device-id}/commands/{command-name}")
public String executeCommandGet(@PathParam("device-id") String deviceId,
@PathParam("command-name") String commandName)
{
this.executeCommand(deviceId, commandName, null);
return "Ok";
}
@Override
@POST
@Path("{device-id}/commands/{command-name}")
@Consumes(MediaType.APPLICATION_JSON)
public void executeCommandPost(@PathParam("device-id") String deviceId,
@PathParam("command-name") String commandName, String commandParameters)
{
this.executeCommand(deviceId, commandName, commandParameters);
}
@Override
@PUT
@Path("{device-id}/commands/{command-name}")
@Consumes(MediaType.APPLICATION_JSON)
public void executeCommandPut(@PathParam("device-id") String deviceId,
@PathParam("command-name") String commandName, String commandParameters)
{
this.executeCommand(deviceId, commandName, commandParameters);
}
/**
*
* @param deviceId
* @param commandName
* @param commandParameters
*/
private void executeCommand(String deviceId, String commandName, String commandParameters)
{
// set default value for the variable used to store the HTTP response by
// the right value: EXPECTATION_FAILED (If something goes wrong we will
// say to user that the command was not executed successfully)
// it was the best response status available
Status response = Response.Status.EXPECTATION_FAILED;
// get the executor instance
Executor executor = Executor.getInstance();
// --- Use Jackson to interpret the type of data passed as value ---
// check if a post/put body is given, it is not an empty JSON object,
// and convert it into an array of parameters
// TODO: check if commands can have more than 1 parameter
if ((commandParameters != null) && (!commandParameters.isEmpty()) && (!commandParameters.equals("{}")))
{
// try to read the payload
for (int i = 0; i < this.payloads.size(); i++)
{
try
{
// try to read the value
CommandPayload<?> payload = this.mapper.readValue(commandParameters, this.payloads.get(i));
// if payload !=null
executor.execute(context, deviceId, commandName, new Object[] { payload.getValue() });
// set the variable used to store the HTTP response by the
// right value
// OK: the command was executed without exception
response = Response.Status.OK;
break;
}
catch (Exception e)
{
// set the variable used to store the HTTP response by the
// right value
// EXPECTATION_FAILED: An exception occured so the command
// was not executed as expected
// it was the best response status available
response = Response.Status.EXPECTATION_FAILED;
// then proceed to the next trial
}
}
}
else
{
// exec the command
try
{
executor.execute(context, deviceId, commandName, new Object[] {});
// set the variable used to store the HTTP response by the right
// value
// OK: the command was executed without exception
response = Response.Status.OK;
}
catch (Exception e)
{
// set the variable used to store the HTTP response by the right
// value
// EXPECTATION_FAILED: An exception occured so the command was
// not executed as expected
// it was the best response status available
response = Response.Status.EXPECTATION_FAILED;
}
}
// launch the exception responsible for sending the HTTP response
throw new WebApplicationException(response);
}
/**
* Generate the XML to be sent
*
* @param dhc
* the {@link DogHomeConfiguration} object to marshall
* @return the corresponding XML
*/
private String generateXML(DogHomeConfiguration dhc)
{
String devicesXML = "";
if (this.jaxbContext != null)
{
try
{
StringWriter output = new StringWriter();
// marshall the DogHomeConfiguration...
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(dhc, output);
devicesXML = output.getBuffer().toString();
}
catch (JAXBException e)
{
// the exception can be throw by the JAXB.marshal method...
this.logger.log(LogService.LOG_ERROR, "Exception in JAXB Marshalling...", e);
}
}
return devicesXML;
}
}