/*
* Copyright Ericsson AB 2011-2014. All Rights Reserved.
*
* The contents of this file are subject to the Lesser GNU Public License,
* (the "License"), either version 2.1 of the License, or
* (at your option) any later version.; you may not use this file except in
* compliance with the License. You should have received a copy of the
* License along with this software. If not, it can be
* retrieved online at https://www.gnu.org/licenses/lgpl.html. Moreover
* it could also be requested from Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
* WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
* EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
* OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
* LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE,
* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
*
* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
* WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
* REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
* DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
* DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
* (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
* INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE
* OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
* HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
*/
package com.ericsson.deviceaccess.coap;
import com.ericsson.common.util.LegacyUtil;
import com.ericsson.deviceaccess.api.GenericDevice;
import com.ericsson.deviceaccess.coap.basedriver.api.CoAPException;
import com.ericsson.deviceaccess.coap.basedriver.api.CoAPRemoteEndpoint;
import com.ericsson.deviceaccess.coap.basedriver.api.CoAPService;
import com.ericsson.deviceaccess.coap.basedriver.api.message.CoAPMessage.CoAPMessageType;
import com.ericsson.deviceaccess.coap.basedriver.api.message.CoAPRequest;
import com.ericsson.deviceaccess.coap.basedriver.api.message.CoAPRequestListener;
import com.ericsson.deviceaccess.coap.basedriver.api.message.CoAPResponse;
import com.ericsson.deviceaccess.coap.basedriver.api.resources.CoAPObservationResource;
import com.ericsson.deviceaccess.coap.basedriver.api.resources.CoAPResource;
import com.ericsson.deviceaccess.coap.basedriver.api.resources.CoAPResourceObserver;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* CoAPDeviceAgent
*
* This class is instantiated by the CoAPDeviceFactory when a new CoAP device is
* discovered. The class creates a HashMap containing the relevant Service
* Schema (in this case, we use the WeatherResource and HelloWorld schema) in
* the update() method and also creates and registers a GenericDeviceImpl object
* with the OSGi framework.
*/
public class CoAPDeviceAgent implements CoAPResourceObserver {
private static final Logger LOGGER = LoggerFactory.getLogger(CoAPDeviceAgent.class);
private final BundleContext context;
private CoAPService coapService;
private Map<URI, List<CoAPDevice>> coap;
public CoAPDeviceAgent(BundleContext bc, CoAPRemoteEndpoint endpoint) {
this.context = bc;
coap = new HashMap<>();
ServiceReference<CoAPService> reference = context.getServiceReference(CoAPService.class);
if (reference != null) {
coapService = context.getService(reference);
} else {
LOGGER.warn("Could still not fetch a reference to CoAPService");
}
// Create an iterator to iterate through the resources a device might have attached to it
endpoint.getResources().keySet().forEach(uri -> {
CoAPDevice temp;
// Check for known resources. This is one of the attributes of the GDA:
// The matching between the device's resources and the service schemas
// is currently statically defined.
switch (uri.getPath()) {
case "/weatherResource":
LOGGER.debug("[CoAPDeviceAgent]: FOUND WEATHER RESOURCE!");
// When a known resource is detected, create a CoAPDevice and attach the resource
temp = new CoAPDevice(uri);
temp.putService(new WeatherResourceImpl(temp));
coap.computeIfAbsent(uri, k -> new ArrayList<>()).add(temp);
// Interface with basedriver to retrieve data from the resource
try {
// Extract the details of the resource from the CoAPResource object
CoAPRequest req = getRequest(uri, CoAPMessageType.CONFIRMABLE);
// Create inner classes to handle possible responses to CoAP request
req.setListener(new CoAPAgentListener(temp));
// Below commented out as CoAP can only handle one request at a time
// This is how CoAP handles QoS
LOGGER.debug("[CoAPDeviceAgent]: sent message!");
// coapService.sendRequest(req);
} catch (CoAPException e) {
LOGGER.error("Creating agent listener failed: ", e);
}
break;
case "/helloWorld":
// HelloWorld resource is how we test CoAP Observations (subscription/notification)
LOGGER.debug("[CoAPDeviceAgent]: FOUND HELLOWORLD RESOURCE!");
// can we move the below statement outside of this if/else clause?
// When a known resource is detected, create a CoAPDevice and attach the resource
temp = new CoAPDevice(uri);
temp.putService(new HelloWorldImpl(temp));
coap.computeIfAbsent(uri, k -> new ArrayList<>()).add(temp);
try {
createObservationRelationship(uri);
} catch (CoAPException e) {
LOGGER.debug("an exception has occurred", e);
}
break;
}
});
}
private void createObservationRelationship(URI uri) throws CoAPException {
// Create Observation relationship between device and the GDA adaptor/basedriver
LOGGER.debug("***** SEND OBSERVE REQUEST *****");
coapService.createObservationRelationship(uri.getHost(), uri.getPort(), "helloWorld", this);
}
public void observeTerminationReceived(CoAPResource resource) {
// Handle Observation terminations
LOGGER.debug("OBSERVER TERMINATION RECEIVED");
}
public void observeResponseReceived(CoAPResponse response,
CoAPResource resource) {
// Handle Observation responses
LOGGER.debug("***** OBSERVER RESPONSE RECEIVED *****");
LOGGER.debug("Observe response received from resource [" + resource + "]");
LOGGER.debug("Response type: " + response.getMessageType().toString());
String payload = new String(resource.getContent(), StandardCharsets.UTF_8);
coap.getOrDefault(resource.getUri(), Collections.emptyList())
.forEach(d -> d.deviceUpdate(payload));
LOGGER.debug("Observation message payload : [" + payload + "]");
}
public void start() {
// Here we actually register the CoAPDevice object to the OSGi framework allowing the rest of the
// GDA framework to interact with the CoAPDevice
LOGGER.info("[CoAPDeviceAgent]: Registering GenericDevImpl");
coap.values().forEach(l -> l.forEach(d -> {
d.setServiceRegistration(context.registerService(GenericDevice.class, d, LegacyUtil.toDictionary(d.getDeviceProperties())));
}));
}
public void stop() {
coap.values().forEach(l -> l.forEach(d -> d.setOnline(false)));
}
public void observeTerminationReceived(CoAPRequest arg0, String arg1) {
// TODO Auto-generated method stub
LOGGER.error("This method was missing, so just added a stub. ");
}
@Override
public void observationRelationshipTerminated(CoAPResponse resp, CoAPObservationResource res, CoAPRequest req) {
// TODO
}
@Override
public void observeResponseReceived(CoAPResponse resp, CoAPResource res, CoAPRequest req) {
//TODO
}
public void resetResponseReceived(CoAPResponse resp, CoAPRequest req) {
//TODO
}
private CoAPRequest getRequest(URI uri, CoAPMessageType type) throws CoAPException {
return coapService.createGetRequest(uri.getHost(), uri.getPort(), uri.getPath(), type);
}
private class CoAPAgentListener implements CoAPRequestListener {
private final CoAPDevice device;
private CoAPAgentListener(CoAPDevice device) {
this.device = device;
}
@Override
public void resetResponseReceived(CoAPResponse resp, CoAPRequest req) {
//TODO
}
@Override
public void emptyAckReceived(CoAPResponse response,
CoAPRequest request) {
// TODO Auto-generated method stub
}
@Override
public void maximumRetransmissionsReached(
CoAPRequest request) {
// TODO Auto-generated method stub
}
@Override
public void piggyPackedResponseReceived(
CoAPResponse response, CoAPRequest request) {
String payload = new String(response.getPayload(), StandardCharsets.UTF_8);
LOGGER.debug("[CoAPDeviceAgent]: Piggypacked message payload: [" + payload + "]");
}
@Override
public void separateResponseReceived(
CoAPResponse response, CoAPRequest request) {
String payload = new String(response.getPayload(), StandardCharsets.UTF_8);
LOGGER.debug("[CoAPDeviceAgent]: Separate message payload: [" + payload + "]");
device.deviceUpdate(payload);
}
@Override
public void serviceBusy(CoAPRequest request) {
// TODO Auto-generated method stub
}
}
}