/*******************************************************************************
* Copyright (c) 2013-2015 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.server.californium.impl;
import static org.eclipse.leshan.core.californium.ResponseCodeUtil.fromCoapCode;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.leshan.Link;
import org.eclipse.leshan.ResponseCode;
import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
import org.eclipse.leshan.core.request.BootstrapFinishRequest;
import org.eclipse.leshan.core.request.BootstrapWriteRequest;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.CreateRequest;
import org.eclipse.leshan.core.request.DeleteRequest;
import org.eclipse.leshan.core.request.DiscoverRequest;
import org.eclipse.leshan.core.request.DownlinkRequestVisitor;
import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.LwM2mRequest;
import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.exception.InvalidResponseException;
import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
import org.eclipse.leshan.core.response.BootstrapFinishResponse;
import org.eclipse.leshan.core.response.BootstrapWriteResponse;
import org.eclipse.leshan.core.response.CreateResponse;
import org.eclipse.leshan.core.response.DeleteResponse;
import org.eclipse.leshan.core.response.DiscoverResponse;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.response.ObserveResponse;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteAttributesResponse;
import org.eclipse.leshan.core.response.WriteResponse;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.util.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LwM2mResponseBuilder<T extends LwM2mResponse> implements DownlinkRequestVisitor {
private static final Logger LOG = LoggerFactory.getLogger(LwM2mResponseBuilder.class);
private LwM2mResponse lwM2mresponse;
private final Request coapRequest;
private final Response coapResponse;
private final ObservationServiceImpl observationService;
private final Registration registration;
private final LwM2mModel model;
private final LwM2mNodeDecoder decoder;
public LwM2mResponseBuilder(Request coapRequest, Response coapResponse, Registration registration, LwM2mModel model,
ObservationServiceImpl observationService, LwM2mNodeDecoder decoder) {
this.coapRequest = coapRequest;
this.coapResponse = coapResponse;
this.observationService = observationService;
this.registration = registration;
this.model = model;
this.decoder = decoder;
}
@Override
public void visit(ReadRequest request) {
switch (coapResponse.getCode()) {
case CONTENT:
LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request,
registration.getEndpoint());
lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, content, null, coapResponse);
break;
case BAD_REQUEST:
case UNAUTHORIZED:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case NOT_ACCEPTABLE:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new ReadResponse(fromCoapCode(coapResponse.getCode().value), null,
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(DiscoverRequest request) {
switch (coapResponse.getCode()) {
case CONTENT:
Link[] links;
if (MediaTypeRegistry.APPLICATION_LINK_FORMAT != coapResponse.getOptions().getContentFormat()) {
LOG.debug("Expected LWM2M Client [{}] to return application/link-format [{}] content but got [{}]",
registration.getEndpoint(), MediaTypeRegistry.APPLICATION_LINK_FORMAT,
coapResponse.getOptions().getContentFormat());
links = new Link[] {}; // empty list
} else {
links = Link.parse(coapResponse.getPayload());
}
lwM2mresponse = new DiscoverResponse(ResponseCode.CONTENT, links, null, coapResponse);
break;
case BAD_REQUEST:
case NOT_FOUND:
case UNAUTHORIZED:
case METHOD_NOT_ALLOWED:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new DiscoverResponse(fromCoapCode(coapResponse.getCode().value), null,
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(WriteRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
lwM2mresponse = new WriteResponse(ResponseCode.CHANGED, null, coapResponse);
break;
case BAD_REQUEST:
case NOT_FOUND:
case UNAUTHORIZED:
case METHOD_NOT_ALLOWED:
case UNSUPPORTED_CONTENT_FORMAT:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new WriteResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(WriteAttributesRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
lwM2mresponse = new WriteAttributesResponse(ResponseCode.CHANGED, null, coapResponse);
break;
case BAD_REQUEST:
case NOT_FOUND:
case UNAUTHORIZED:
case METHOD_NOT_ALLOWED:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new WriteAttributesResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(ExecuteRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
lwM2mresponse = new ExecuteResponse(ResponseCode.CHANGED, null, coapResponse);
break;
case BAD_REQUEST:
case UNAUTHORIZED:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new ExecuteResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(CreateRequest request) {
switch (coapResponse.getCode()) {
case CREATED:
lwM2mresponse = new CreateResponse(ResponseCode.CREATED, coapResponse.getOptions().getLocationPathString(),
null, coapResponse);
break;
case BAD_REQUEST:
case UNAUTHORIZED:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case UNSUPPORTED_CONTENT_FORMAT:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new CreateResponse(fromCoapCode(coapResponse.getCode().value), null,
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(DeleteRequest request) {
switch (coapResponse.getCode()) {
case DELETED:
lwM2mresponse = new DeleteResponse(ResponseCode.DELETED, null, coapResponse);
break;
case UNAUTHORIZED:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case BAD_REQUEST:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new DeleteResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(ObserveRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
// TODO now the spec say that NOTIFY should use 2.05 content so we should remove this.
// ignore changed response (this is probably a NOTIFY)
lwM2mresponse = null;
break;
case CONTENT:
LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request,
registration.getEndpoint());
if (coapResponse.getOptions().hasObserve()) {
// observe request successful
Observation observation = new Observation(coapRequest.getToken(), registration.getId(),
request.getPath(), request.getContext());
observationService.addObservation(registration, observation);
// add the observation to an ObserveResponse instance
lwM2mresponse = new ObserveResponse(ResponseCode.CONTENT, content, null, observation, null,
coapResponse);
} else {
lwM2mresponse = new ObserveResponse(ResponseCode.CONTENT, content, null, null, null, coapResponse);
}
break;
case BAD_REQUEST:
case UNAUTHORIZED:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case NOT_ACCEPTABLE:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new ObserveResponse(fromCoapCode(coapResponse.getCode().value), null, null, null,
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(BootstrapWriteRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
lwM2mresponse = new BootstrapWriteResponse(ResponseCode.CHANGED, null, coapResponse);
break;
case UNSUPPORTED_CONTENT_FORMAT:
case BAD_REQUEST:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new BootstrapWriteResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(BootstrapDeleteRequest request) {
switch (coapResponse.getCode()) {
case DELETED:
lwM2mresponse = new BootstrapDeleteResponse(ResponseCode.DELETED, null, coapResponse);
break;
case BAD_REQUEST:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new BootstrapDeleteResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
@Override
public void visit(BootstrapFinishRequest request) {
switch (coapResponse.getCode()) {
case CHANGED:
lwM2mresponse = new BootstrapFinishResponse(ResponseCode.CHANGED, null, coapResponse);
break;
case BAD_REQUEST:
case INTERNAL_SERVER_ERROR:
lwM2mresponse = new BootstrapFinishResponse(fromCoapCode(coapResponse.getCode().value),
coapResponse.getPayloadString(), coapResponse);
break;
default:
handleUnexpectedResponseCode(registration.getEndpoint(), request, coapResponse);
}
}
private LwM2mNode decodeCoapResponse(LwM2mPath path, Response coapResponse, LwM2mRequest<?> request,
String endpoint) {
// Get content format
ContentFormat contentFormat = null;
if (coapResponse.getOptions().hasContentFormat()) {
contentFormat = ContentFormat.fromCode(coapResponse.getOptions().getContentFormat());
}
// Decode payload
try {
return decoder.decode(coapResponse.getPayload(), contentFormat, path, model);
} catch (CodecException e) {
if (LOG.isDebugEnabled()) {
byte[] payload = coapResponse.getPayload() == null ? new byte[0] : coapResponse.getPayload();
LOG.debug(
String.format("Unable to decode response payload of request [%s] from client [%s] [payload:%s]",
request, endpoint, Hex.encodeHexString(payload)));
}
throw new InvalidResponseException(e, "Unable to decode response payload of request [%s] from client [%s]",
request, endpoint);
}
}
@SuppressWarnings("unchecked")
public T getResponse() {
return (T) lwM2mresponse;
}
private void handleUnexpectedResponseCode(String clientEndpoint, LwM2mRequest<?> request, Response coapResponse) {
throw new InvalidResponseException("Client [%s] returned unexpected response code [%s] for [%s]",
clientEndpoint, coapResponse.getCode(), request);
}
}