All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.leshan.client.resource.BaseObjectEnabler Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 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
 *     Achim Kraus (Bosch Software Innovations GmbH) - deny delete of resource
 *     Achim Kraus (Bosch Software Innovations GmbH) - use ServerIdentity to 
 *                                                     protect the security object
 *     Achim Kraus (Bosch Software Innovations GmbH) - add resource checks for 
 *                                                     REPLACE/UPDAT implementation
 *******************************************************************************/
package org.eclipse.leshan.client.resource;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.leshan.Link;
import org.eclipse.leshan.LwM2mId;
import org.eclipse.leshan.client.request.ServerIdentity;
import org.eclipse.leshan.client.util.LinkFormatHelper;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
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.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest;
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.response.BootstrapDeleteResponse;
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.ObserveResponse;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteAttributesResponse;
import org.eclipse.leshan.core.response.WriteResponse;

public abstract class BaseObjectEnabler implements LwM2mObjectEnabler {

    final int id;
    private NotifySender notifySender;
    private ObjectModel objectModel;

    public BaseObjectEnabler(int id, ObjectModel objectModel) {
        this.id = id;
        this.objectModel = objectModel;
    }

    @Override
    public synchronized int getId() {
        return id;
    }

    @Override
    public synchronized ObjectModel getObjectModel() {
        return objectModel;
    }

    @Override
    public List getAvailableResourceIds(int instanceId) {
        // By default we consider that all resources defined in the model are supported
        ArrayList resourceIds = new ArrayList<>(objectModel.resources.keySet());
        Collections.sort(resourceIds);
        return resourceIds;
    }

    @Override
    public synchronized CreateResponse create(ServerIdentity identity, CreateRequest request) {
        if (!identity.isSystem()) {
            if (id == LwM2mId.SECURITY) {
                return CreateResponse.notFound();
            }
        }
        return doCreate(identity, request);
    }

    protected CreateResponse doCreate(ServerIdentity identity, CreateRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return CreateResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized ReadResponse read(ServerIdentity identity, ReadRequest request) {
        LwM2mPath path = request.getPath();

        // read is not supported for bootstrap
        if (identity.isLwm2mBootstrapServer()) {
            return ReadResponse.methodNotAllowed();
        }

        if (!identity.isSystem()) {
            // read the security object is forbidden
            if (id == LwM2mId.SECURITY) {
                return ReadResponse.notFound();
            }

            // check if the resource is readable.
            if (path.isResource()) {
                ResourceModel resourceModel = objectModel.resources.get(path.getResourceId());
                if (resourceModel != null && !resourceModel.operations.isReadable()) {
                    return ReadResponse.methodNotAllowed();
                }
            }
        }

        return doRead(identity, request);

        // TODO we could do a validation of response.getContent by comparing with the spec.
    }

    protected ReadResponse doRead(ServerIdentity identity, ReadRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return ReadResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized WriteResponse write(ServerIdentity identity, WriteRequest request) {
        LwM2mPath path = request.getPath();

        // write is not supported for bootstrap, use bootstrap write
        if (identity.isLwm2mBootstrapServer()) {
            return WriteResponse.methodNotAllowed();
        }

        // write the security object is forbidden
        if (LwM2mId.SECURITY == id && !identity.isSystem()) {
            return WriteResponse.notFound();
        }

        if (path.isResource()) {
            // resource write:
            // check if the resource is writable
            if (LwM2mId.SECURITY != id) { // security resources are writable by SYSTEM
                ResourceModel resourceModel = objectModel.resources.get(path.getResourceId());
                if (resourceModel != null && !resourceModel.operations.isWritable()) {
                    return WriteResponse.methodNotAllowed();
                }
            }
        } else if (path.isObjectInstance()) {
            // instance write:
            // check if all resources are writable
            if (LwM2mId.SECURITY != id) { // security resources are writable by SYSTEM
                ObjectModel model = getObjectModel();
                for (Integer writeResourceId : ((LwM2mObjectInstance) request.getNode()).getResources().keySet()) {
                    ResourceModel resourceModel = model.resources.get(writeResourceId);
                    if (null != resourceModel && !resourceModel.operations.isWritable()) {
                        return WriteResponse.methodNotAllowed();
                    }
                }
            }

            if (request.isReplaceRequest()) {
                // REPLACE
                // check, if all mandatory writable resources are provided
                // Collect all mandatory writable resource IDs from the model
                Set mandatoryResources = new HashSet<>();
                for (ResourceModel resourceModel : getObjectModel().resources.values()) {
                    if (resourceModel.mandatory && (LwM2mId.SECURITY == id || resourceModel.operations.isWritable()))
                        mandatoryResources.add(resourceModel.id);
                }
                // Afterwards remove the provided resource IDs from that set
                for (Integer writeResourceId : ((LwM2mObjectInstance) request.getNode()).getResources().keySet()) {
                    mandatoryResources.remove(writeResourceId);
                }
                if (!mandatoryResources.isEmpty()) {
                    return WriteResponse.badRequest("mandatory writable resources missing!");
                }
            }
        }

        // TODO we could do a validation of request.getNode() by comparing with resourceSpec information

        return doWrite(identity, request);
    }

    protected WriteResponse doWrite(ServerIdentity identity, WriteRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return WriteResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized BootstrapWriteResponse write(ServerIdentity identity, BootstrapWriteRequest request) {

        // We should not get a bootstrapWriteRequest from a LWM2M server
        if (!identity.isLwm2mBootstrapServer()) {
            return BootstrapWriteResponse.internalServerError("bootstrap write request from LWM2M server");
        }

        return doWrite(identity, request);
    }

    protected BootstrapWriteResponse doWrite(ServerIdentity identity, BootstrapWriteRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return BootstrapWriteResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized DeleteResponse delete(ServerIdentity identity, DeleteRequest request) {
        if (!identity.isSystem()) {

            // delete the security object is forbidden
            if (id == LwM2mId.SECURITY) {
                return DeleteResponse.notFound();
            }

            if (id == LwM2mId.DEVICE) {
                return DeleteResponse.methodNotAllowed();
            }
        }

        return doDelete(identity, request);
    }

    protected DeleteResponse doDelete(ServerIdentity identity, DeleteRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return DeleteResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized BootstrapDeleteResponse delete(ServerIdentity identity, BootstrapDeleteRequest request) {
        if (!identity.isSystem()) {
            if (id == LwM2mId.DEVICE) {
                return BootstrapDeleteResponse.badRequest("Device object instance is not deletable");
            }
        }
        return doDelete(identity, request);
    }

    public BootstrapDeleteResponse doDelete(ServerIdentity identity, BootstrapDeleteRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return BootstrapDeleteResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized ExecuteResponse execute(ServerIdentity identity, ExecuteRequest request) {
        LwM2mPath path = request.getPath();

        // execute is not supported for bootstrap
        if (identity.isLwm2mBootstrapServer()) {
            return ExecuteResponse.methodNotAllowed();
        }

        // execute on security object is forbidden
        if (id == LwM2mId.SECURITY) {
            return ExecuteResponse.notFound();
        }

        // only resource could be executed
        if (!path.isResource()) {
            return ExecuteResponse.badRequest(null);
        }

        // check if the resource is writable
        ResourceModel resourceModel = objectModel.resources.get(path.getResourceId());
        if (resourceModel != null && !resourceModel.operations.isExecutable()) {
            return ExecuteResponse.methodNotAllowed();
        }

        return doExecute(identity, request);
    }

    protected ExecuteResponse doExecute(ServerIdentity identity, ExecuteRequest request) {
        // This should be a not implemented error, but this is not defined in the spec.
        return ExecuteResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized WriteAttributesResponse writeAttributes(ServerIdentity identity,
            WriteAttributesRequest request) {
        // TODO should be implemented here to be available for all object enabler
        // This should be a not implemented error, but this is not defined in the spec.
        return WriteAttributesResponse.internalServerError("not implemented");
    }

    @Override
    public synchronized DiscoverResponse discover(ServerIdentity identity, DiscoverRequest request) {

        if (identity.isLwm2mBootstrapServer()) {
            // discover is not supported for bootstrap
            return DiscoverResponse.methodNotAllowed();
        }

        if (id == LwM2mId.SECURITY) {
            return DiscoverResponse.notFound();
        }
        return doDiscover(identity, request);

    }

    protected DiscoverResponse doDiscover(ServerIdentity identity, DiscoverRequest request) {

        LwM2mPath path = request.getPath();
        if (path.isObject()) {
            // Manage discover on object
            Link[] ObjectLinks = LinkFormatHelper.getObjectDescription(this, null);
            return DiscoverResponse.success(ObjectLinks);

        } else if (path.isObjectInstance()) {
            // Manage discover on instance
            if (!getAvailableInstanceIds().contains(path.getObjectInstanceId()))
                return DiscoverResponse.notFound();

            Link[] instanceLink = LinkFormatHelper.getInstanceDescription(this, path.getObjectInstanceId(), null);
            return DiscoverResponse.success(instanceLink);

        } else if (path.isResource()) {
            // Manage discover on resource
            if (!getAvailableInstanceIds().contains(path.getObjectInstanceId()))
                return DiscoverResponse.notFound();

            ResourceModel resourceModel = getObjectModel().resources.get(path.getResourceId());
            if (resourceModel == null)
                return DiscoverResponse.notFound();

            if (!getAvailableResourceIds(path.getObjectInstanceId()).contains(path.getResourceId()))
                return DiscoverResponse.notFound();

            Link resourceLink = LinkFormatHelper.getResourceDescription(this, path.getObjectInstanceId(),
                    path.getResourceId(), null);
            return DiscoverResponse.success(new Link[] { resourceLink });
        }
        return DiscoverResponse.badRequest(null);
    }

    @Override
    public synchronized ObserveResponse observe(ServerIdentity identity, ObserveRequest request) {
        LwM2mPath path = request.getPath();

        // observe is not supported for bootstrap
        if (identity.isLwm2mBootstrapServer())
            return ObserveResponse.methodNotAllowed();

        if (!identity.isSystem()) {
            // observe or read of the security object is forbidden
            if (id == LwM2mId.SECURITY)
                return ObserveResponse.notFound();

            // check if the resource is readable.
            if (path.isResource()) {
                ResourceModel resourceModel = objectModel.resources.get(path.getResourceId());
                if (resourceModel != null && !resourceModel.operations.isReadable())
                    return ObserveResponse.methodNotAllowed();
            }
        }
        return doObserve(identity, request);
    }

    protected ObserveResponse doObserve(ServerIdentity identity, ObserveRequest request) {
        ReadResponse readResponse = this.read(identity, new ReadRequest(request.getPath().toString()));
        return new ObserveResponse(readResponse.getCode(), readResponse.getContent(), null, null,
                readResponse.getErrorMessage());
    }

    @Override
    public void setNotifySender(NotifySender sender) {
        notifySender = sender;
    }

    public NotifySender getNotifySender() {
        return notifySender;
    }

    @Override
    public ContentFormat getDefaultEncodingFormat(DownlinkRequest request) {
        return ContentFormat.DEFAULT;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy