/*******************************************************************************
* 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.core.request;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import org.eclipse.leshan.core.model.ResourceModel.Type;
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.request.exception.InvalidRequestException;
import org.eclipse.leshan.core.response.WriteResponse;
/**
* The request to change the value of a Resource, an array of Resources Instances or multiple Resources from an Object
* Instance.
*/
public class WriteRequest extends AbstractDownlinkRequest<WriteResponse> {
/**
* Define the behavior of a write request.
*/
public enum Mode {
/**
* Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
* section 5.3.3 of the LW M2M spec).
*/
REPLACE,
/**
* Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see
* section 5.3.3 of the LW M2M spec).
*/
UPDATE
}
private final LwM2mNode node;
private final ContentFormat contentFormat;
private final Mode mode;
// ***************** write instance ****************** //
/**
* Request to write an <b>Object Instance</b>.
*
* @param mode the mode of the request : replace or update.
* @param contentFormat Format of the payload (TLV or JSON).
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resources the list of resources to write.
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(Mode mode, ContentFormat contentFormat, int objectId, int objectInstanceId,
Collection<LwM2mResource> resources) throws InvalidRequestException {
this(mode, contentFormat, new LwM2mPath(objectId, objectInstanceId),
new LwM2mObjectInstance(objectId, resources));
}
/**
* Request to write an <b>Object Instance</b> using the TLV content format.
*
* @param mode the mode of the request : replace or update.
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resources the list of resources to write.
*/
public WriteRequest(Mode mode, int objectId, int objectInstanceId, Collection<LwM2mResource> resources) {
this(mode, ContentFormat.TLV, new LwM2mPath(objectId, objectInstanceId),
new LwM2mObjectInstance(objectId, resources));
}
/**
* Request to write an <b>Object Instance</b>.
*
* @param mode the mode of the request : replace or update.
* @param contentFormat Format of the payload (TLV or JSON).
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resources the list of resources to write.
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(Mode mode, ContentFormat contentFormat, int objectId, int objectInstanceId,
LwM2mResource... resources) throws InvalidRequestException {
this(mode, contentFormat, new LwM2mPath(objectId, objectInstanceId),
new LwM2mObjectInstance(objectId, resources));
}
/**
* Request to write an <b>Object Instance</b> using the TLV content format.
*
* @param mode the mode of the request : replace or update.
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resources the list of resources to write.
*/
public WriteRequest(Mode mode, int objectId, int objectInstanceId, LwM2mResource... resources) {
this(mode, ContentFormat.TLV, new LwM2mPath(objectId, objectInstanceId),
new LwM2mObjectInstance(objectId, resources));
}
// ***************** write single value resource ****************** //
/**
* Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, String value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, String value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newStringResource(resourceId, value));
}
/**
* Request to write a <b>Boolean Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, boolean value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b>Boolean Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, boolean value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newBooleanResource(resourceId, value));
}
/**
* Request to write a <b>Integer Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, long value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b>Integer Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, long value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newIntegerResource(resourceId, value));
}
/**
* Request to write a <b> Float Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, double value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b> Float Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, double value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newFloatResource(resourceId, value));
}
/**
* Request to write a <b> Date Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, Date value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b> Date Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, Date value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newDateResource(resourceId, value));
}
/**
* Request to write a <b> Binary Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, byte[] value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b> Binary Single-Instance Resource</b> using the given content format (OPAQUE, TLV, JSON).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, byte[] value)
throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newBinaryResource(resourceId, value));
}
/**
* Request to write a <b> Objlnk Single-Instance Resource</b> using the TLV content format.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, ObjectLink value) {
this(ContentFormat.TLV, objectId, objectInstanceId, resourceId, value);
}
/**
* Request to write a <b> Objlnk Single-Instance Resource</b> using the given content format (TLV, JSON, TEXT).
*
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId,
ObjectLink value) throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mSingleResource.newObjectLinkResource(resourceId, value));
}
// ***************** write multi instance resource ****************** //
/**
* Request to write a <b>Multi-Instance Resource</b>.
*
* @param contentFormat Format of the payload (TLV or JSON).
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resourceId the id of the resource to write.
* @param values the list of resource instance (id->value) to write.
* @param type the data type of the resource.
* @exception InvalidRequestException if bad @{link ContentFormat} format was used.
*
*/
public WriteRequest(ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId,
Map<Integer, ?> values, Type type) throws InvalidRequestException {
this(Mode.REPLACE, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mMultipleResource.newResource(resourceId, values, type));
}
/**
* Request to write a <b>Multi-Instance Resource</b> using the TLV content format.
*
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resourceId the id of the resource to write.
* @param values the list of resource instance (id->value) to write.
* @param type the data type of the resource.
* @exception InvalidRequestException if parameters are invalid.
*/
public WriteRequest(int objectId, int objectInstanceId, int resourceId, Map<Integer, ?> values, Type type)
throws InvalidRequestException {
this(Mode.REPLACE, ContentFormat.TLV, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mMultipleResource.newResource(resourceId, values, type));
}
// ***************** generic constructor ****************** //
/**
* A generic constructor to write request.
*
* @param mode the mode of the request : replace or update.
* @param contentFormat Format of the payload (TLV,JSON,TEXT,OPAQUE ..).
* @param path the path of the LWM2M node to write (object instance or resource).
* @param node the {@link LwM2mNode} to write.
* @exception InvalidRequestException if parameters are invalid.
*/
public WriteRequest(Mode mode, ContentFormat contentFormat, String path, LwM2mNode node)
throws InvalidRequestException {
this(mode, contentFormat, newPath(path), node);
}
private WriteRequest(Mode mode, ContentFormat format, LwM2mPath target, LwM2mNode node) {
super(target);
if (mode == null)
throw new InvalidRequestException("mode is mandatory for %s", target);
if (node == null)
throw new InvalidRequestException("new node value is mandatory for %s", target);
// Validate Mode
if (getPath().isResource() && mode == Mode.UPDATE)
throw new InvalidRequestException("Invalid mode for '%s': update is not allowed on resource", target);
// Validate node and path coherence
if (getPath().isResource()) {
if (!(node instanceof LwM2mResource)) {
throw new InvalidRequestException("path '%s' and node type '%s' do not match", target,
node.getClass().getSimpleName());
}
} else if (getPath().isObjectInstance()) {
if (!(node instanceof LwM2mObjectInstance)) {
throw new InvalidRequestException("path '%s' and node type '%s' do not match", target,
node.getClass().getSimpleName());
}
} else if (getPath().isObject()) {
throw new InvalidRequestException("write request %s cannot target an object", target);
}
// Validate content format
if (ContentFormat.TEXT == format || ContentFormat.OPAQUE == format) {
if (!getPath().isResource()) {
throw new InvalidRequestException(
"Invalid format for %s: %s format must be used only for single resources", target, format);
} else {
LwM2mResource resource = (LwM2mResource) node;
if (resource.isMultiInstances()) {
throw new InvalidRequestException(
"Invalid format for path %s: format must be used only for single resources", target,
format);
} else {
if (resource.getType() == Type.OPAQUE && format == ContentFormat.TEXT) {
throw new InvalidRequestException(
"Invalid format for %s: TEXT format must not be used for byte array single resources",
target);
} else if (resource.getType() != Type.OPAQUE && format == ContentFormat.OPAQUE) {
throw new InvalidRequestException(
"Invalid format for %s: OPAQUE format must be used only for byte array single resources",
target);
}
}
}
}
this.node = node;
if (format == null) {
this.contentFormat = ContentFormat.TLV; // use TLV as default content type
} else {
this.contentFormat = format;
}
this.mode = mode;
}
/**
* @return <code>true</code> if all resources are to be replaced (see section 5.3.3 of the LW M2M spec).
*/
public boolean isReplaceRequest() {
return mode == Mode.REPLACE;
}
/**
* @return <code>true</code> if this is a partial update request (see section 5.3.3 of the LW M2M spec).
*/
public boolean isPartialUpdateRequest() {
return mode == Mode.UPDATE;
}
public LwM2mNode getNode() {
return node;
}
public ContentFormat getContentFormat() {
return contentFormat;
}
@Override
public void accept(DownlinkRequestVisitor visitor) {
visitor.visit(this);
}
@Override
public String toString() {
return String.format("WriteRequest [mode=%s, path=%s, format=%s, node=%s]", mode, getPath(), contentFormat,
node);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((contentFormat == null) ? 0 : contentFormat.hashCode());
result = prime * result + ((mode == null) ? 0 : mode.hashCode());
result = prime * result + ((node == null) ? 0 : node.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
WriteRequest other = (WriteRequest) obj;
if (contentFormat != other.contentFormat)
return false;
if (mode != other.mode)
return false;
if (node == null) {
if (other.node != null)
return false;
} else if (!node.equals(other.node))
return false;
return true;
}
}