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

org.eclipse.leshan.client.californium.LeshanClient Maven / Gradle / Ivy

There is a newer version: 2.0.0-M15
Show newest version
/*******************************************************************************
 * 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 v2.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * 
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 * 
 * Contributors:
 *     Zebra Technologies - initial API and implementation
 *******************************************************************************/
package org.eclipse.leshan.client.californium;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.elements.util.ExecutorsUtil;
import org.eclipse.californium.elements.util.NamedThreadFactory;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder;
import org.eclipse.leshan.client.LwM2mClient;
import org.eclipse.leshan.client.RegistrationUpdateHandler;
import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
import org.eclipse.leshan.client.californium.bootstrap.BootstrapResource;
import org.eclipse.leshan.client.californium.object.ObjectResource;
import org.eclipse.leshan.client.californium.request.CaliforniumLwM2mRequestSender;
import org.eclipse.leshan.client.engine.RegistrationEngine;
import org.eclipse.leshan.client.engine.RegistrationEngineFactory;
import org.eclipse.leshan.client.engine.RegistrationEngineFactory2;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.observer.LwM2mClientObserverAdapter;
import org.eclipse.leshan.client.observer.LwM2mClientObserverDispatcher;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
import org.eclipse.leshan.client.resource.listener.ObjectListener;
import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.californium.EndpointFactory;
import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
import org.eclipse.leshan.core.util.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Lightweight M2M client based on Californium (CoAP implementation) and Scandium (DTLS implementation) which supports
 * only 1 LWM2M server.
 */
public class LeshanClient implements LwM2mClient {

    private static final Logger LOG = LoggerFactory.getLogger(LeshanClient.class);

    private final CoapAPI coapApi;
    private final CoapServer coapServer;
    private final CaliforniumLwM2mRequestSender requestSender;
    private final CaliforniumEndpointsManager endpointsManager;

    private LwM2mObjectTree objectTree;
    private final BootstrapHandler bootstrapHandler;
    private final RegistrationEngine engine;
    private final LwM2mClientObserverDispatcher observers;

    public LeshanClient(String endpoint, InetSocketAddress localAddress,
            List objectEnablers, NetworkConfig coapConfig, Builder dtlsConfigBuilder,
            EndpointFactory endpointFactory, RegistrationEngineFactory engineFactory,
            Map additionalAttributes, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder,
            ScheduledExecutorService sharedExecutor) {
        this(endpoint, localAddress, objectEnablers, coapConfig, dtlsConfigBuilder, endpointFactory, engineFactory,
                additionalAttributes, null, encoder, decoder, sharedExecutor);
    }

    /** @since 1.1 */
    public LeshanClient(String endpoint, InetSocketAddress localAddress,
            List objectEnablers, NetworkConfig coapConfig, Builder dtlsConfigBuilder,
            EndpointFactory endpointFactory, RegistrationEngineFactory engineFactory,
            Map additionalAttributes, Map bsAdditionalAttributes,
            LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder, ScheduledExecutorService sharedExecutor) {

        Validate.notNull(endpoint);
        Validate.notEmpty(objectEnablers);
        Validate.notNull(coapConfig);

        objectTree = createObjectTree(objectEnablers);
        observers = createClientObserverDispatcher();
        bootstrapHandler = createBoostrapHandler(objectTree);
        endpointsManager = createEndpointsManager(localAddress, coapConfig, dtlsConfigBuilder, endpointFactory);
        requestSender = createRequestSender(endpointsManager, sharedExecutor);
        if (engineFactory instanceof RegistrationEngineFactory2) {
            engine = ((RegistrationEngineFactory2) engineFactory).createRegistratioEngine(endpoint, objectTree,
                    endpointsManager, requestSender, bootstrapHandler, observers, additionalAttributes,
                    bsAdditionalAttributes, sharedExecutor);
        } else {
            engine = engineFactory.createRegistratioEngine(endpoint, objectTree, endpointsManager, requestSender,
                    bootstrapHandler, observers, additionalAttributes, sharedExecutor);
        }

        coapServer = createCoapServer(coapConfig, sharedExecutor);
        coapServer.add(createBootstrapResource(engine, bootstrapHandler));
        endpointsManager.setCoapServer(coapServer);
        linkObjectTreeToCoapServer(coapServer, engine, objectTree, encoder, decoder);
        createRegistrationUpdateHandler(engine, endpointsManager, bootstrapHandler, objectTree);

        coapApi = new CoapAPI();
    }

    protected LwM2mObjectTree createObjectTree(List objectEnablers) {
        return new LwM2mObjectTree(this, objectEnablers);
    }

    protected LwM2mClientObserverDispatcher createClientObserverDispatcher() {
        LwM2mClientObserverDispatcher observer = new LwM2mClientObserverDispatcher();
        observer.addObserver(new LwM2mClientObserverAdapter() {
            @Override
            public void onUnexpectedError(Throwable unexpectedError) {
                LeshanClient.this.destroy(false);
            }
        });
        return observer;
    }

    protected BootstrapHandler createBoostrapHandler(LwM2mObjectTree objectTree) {
        return new BootstrapHandler(objectTree.getObjectEnablers());
    }

    protected CoapServer createCoapServer(NetworkConfig coapConfig, ScheduledExecutorService sharedExecutor) {
        // create coap server
        CoapServer coapServer = new CoapServer(coapConfig) {
            @Override
            protected Resource createRoot() {
                // Use to handle Delete on "/"
                return new org.eclipse.leshan.client.californium.RootResource(engine, bootstrapHandler, this);
            }
        };

        // configure executors
        if (sharedExecutor != null) {
            coapServer.setExecutors(sharedExecutor, sharedExecutor, true);
        } else {
            // use same executor as main and secondary one.
            ScheduledExecutorService executor = ExecutorsUtil.newScheduledThreadPool(
                    coapConfig.getInt(NetworkConfig.Keys.PROTOCOL_STAGE_THREAD_COUNT),
                    new NamedThreadFactory("CoapServer(main)#"));
            coapServer.setExecutors(executor, executor, false);
        }
        return coapServer;
    }

    protected void linkObjectTreeToCoapServer(final CoapServer coapServer, final RegistrationEngine registrationEngine,
            LwM2mObjectTree objectTree, final LwM2mNodeEncoder encoder, final LwM2mNodeDecoder decoder) {

        // Create CoAP resources for each lwm2m Objects.
        for (LwM2mObjectEnabler enabler : objectTree.getObjectEnablers().values()) {
            CoapResource clientObject = createObjectResource(enabler, registrationEngine, encoder, decoder);
            coapServer.add(clientObject);
        }

        // listen object tree
        objectTree.addListener(new ObjectsListenerAdapter() {
            @Override
            public void objectAdded(LwM2mObjectEnabler object) {
                CoapResource clientObject = createObjectResource(object, registrationEngine, encoder, decoder);
                coapServer.add(clientObject);
            }

            @Override
            public void objectRemoved(LwM2mObjectEnabler object) {
                Resource resource = coapServer.getRoot().getChild(Integer.toString(object.getId()));
                if (resource instanceof ObjectListener) {
                    object.removeListener((ObjectListener) (resource));
                }
                coapServer.remove(resource);
            }
        });
    }

    protected CoapResource createObjectResource(LwM2mObjectEnabler enabler, RegistrationEngine registrationEngine,
            LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
        return new ObjectResource(enabler, registrationEngine, encoder, decoder);
    }

    protected CoapResource createBootstrapResource(RegistrationEngine engine, BootstrapHandler bootstrapHandler) {
        return new BootstrapResource(engine, bootstrapHandler);
    }

    protected CaliforniumEndpointsManager createEndpointsManager(InetSocketAddress localAddress,
            NetworkConfig coapConfig, Builder dtlsConfigBuilder, EndpointFactory endpointFactory) {
        return new CaliforniumEndpointsManager(localAddress, coapConfig, dtlsConfigBuilder, endpointFactory);
    }

    protected CaliforniumLwM2mRequestSender createRequestSender(CaliforniumEndpointsManager endpointsManager,
            ScheduledExecutorService executor) {
        return new CaliforniumLwM2mRequestSender(endpointsManager, executor);
    }

    protected RegistrationUpdateHandler createRegistrationUpdateHandler(RegistrationEngine engine,
            CaliforniumEndpointsManager endpointsManager, BootstrapHandler bootstrapHandler,
            LwM2mObjectTree objectTree) {
        RegistrationUpdateHandler registrationUpdateHandler = new RegistrationUpdateHandler(engine, bootstrapHandler);
        registrationUpdateHandler.listen(objectTree);
        return registrationUpdateHandler;
    }

    @Override
    public void start() {
        LOG.info("Starting Leshan client ...");
        endpointsManager.start();
        engine.start();
        objectTree.start();
        if (LOG.isInfoEnabled()) {
            LOG.info("Leshan client[endpoint:{}] started.", engine.getEndpoint());
        }
    }

    @Override
    public void stop(boolean deregister) {
        LOG.info("Stopping Leshan Client ...");
        engine.stop(deregister);
        endpointsManager.stop();
        objectTree.stop();
        LOG.info("Leshan client stopped.");
    }

    @Override
    public void destroy(boolean deregister) {
        LOG.info("Destroying Leshan client ...");
        engine.destroy(deregister);
        endpointsManager.destroy();
        requestSender.destroy();
        objectTree.destroy();
        LOG.info("Leshan client destroyed.");
    }

    @Override
    public LwM2mObjectTree getObjectTree() {
        return objectTree;
    }

    @Override
    public void triggerRegistrationUpdate() {
        engine.triggerRegistrationUpdate();
    }

    @Override
    public void triggerRegistrationUpdate(ServerIdentity server) {
        engine.triggerRegistrationUpdate(server);
    }

    /**
     * A CoAP API, generally needed if you want to access to underlying CoAP layer.
     */
    public CoapAPI coap() {
        return coapApi;
    }

    public class CoapAPI {
        /**
         * @return the underlying {@link CoapServer}
         */
        public CoapServer getServer() {
            return coapServer;
        }

        /**
         * Returns the current {@link CoapEndpoint} used to communicate with the given server.
         * 
         * @return the {@link CoapEndpoint} used to communicate to LWM2M server.
         */
        public CoapEndpoint getEndpoint(ServerIdentity server) {
            return (CoapEndpoint) endpointsManager.getEndpoint(server);
        }
    }

    /**
     * Add listener to observe client lifecycle (bootstrap, register, update, deregister).
     */
    public void addObserver(LwM2mClientObserver observer) {
        observers.addObserver(observer);
    }

    /**
     * Remove the given {@link LwM2mClientObserver}.
     */
    public void removeObserver(LwM2mClientObserver observer) {
        observers.removeObserver(observer);
    }

    /**
     * Returns the registration Id for the given server.
     * 
     * @return the client registration Id or null if the client is not registered
     */
    public String getRegistrationId(ServerIdentity server) {
        return engine.getRegistrationId(server);
    }

    /**
     * @return All the registered Server indexed by the corresponding registration id;
     */
    public Map getRegisteredServers() {
        return engine.getRegisteredServers();
    }

    /**
     * Returns the current {@link InetSocketAddress} use to communicate with the given server.
     * 
     * @return the address used to connect to the server or null if the client is not started.
     */
    public InetSocketAddress getAddress(ServerIdentity server) {
        return endpointsManager.getEndpoint(server).getAddress();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy