Package de.uniluebeck.itm.ncoap.application.client

Source Code of de.uniluebeck.itm.ncoap.application.client.CoapClientApplication

/**
* Copyright (c) 2012, Oliver Kleine, Institute of Telematics, University of Luebeck
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
*  - Redistributions of source messageCode must retain the above copyright notice, this list of conditions and the following
*    disclaimer.
*
*  - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
*    following disclaimer in the documentation and/or other materials provided with the distribution.
*
*  - Neither the name of the University of Luebeck nor the names of its contributors may be used to endorse or promote
*    products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package de.uniluebeck.itm.ncoap.application.client;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import de.uniluebeck.itm.ncoap.communication.dispatching.client.TokenFactory;
import de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback;
import de.uniluebeck.itm.ncoap.communication.dispatching.client.OutboundMessageWrapper;
import de.uniluebeck.itm.ncoap.communication.reliability.OutboundReliabilityHandler;
import de.uniluebeck.itm.ncoap.message.*;
import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.DatagramChannel;
import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory;
import org.jboss.netty.util.ThreadNameDeterminer;
import org.jboss.netty.util.ThreadRenamingRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;

/**
* An instance of {@link CoapClientApplication} is the entry point to send {@link CoapMessage}s to a (remote)
* server or proxy.
*
* With {@link #sendCoapRequest(CoapRequest, de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback, InetSocketAddress)} it e.g. provides an
* easy-to-use method to write CoAP requests to a server.
*
* Furthermore, with {@link #sendCoapPing(de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback, java.net.InetSocketAddress)} it provides a method to test
* if a remote CoAP endpoint (i.e. the CoAP application and not only the host(!)) is alive.
*
* @author Oliver Kleine
*/
public class CoapClientApplication {

    public static final int RECEIVE_BUFFER_SIZE = 65536;

    private Logger log = LoggerFactory.getLogger(this.getClass().getName());

    private ScheduledThreadPoolExecutor exeutor;
    private DatagramChannel channel;

    private String name;

    /**
     * Creates a new instance of {@link CoapClientApplication}.
     *
     * @param name the name of the application (used for logging purposes)
     * @param port the port, this {@link CoapClientApplication} should be bound to (use <code>0</code> for
     *             arbitrary port)
     * @param numberOfThreads the number of threads to be used for I/O operations. The minimum number is 4, i.e. even
     *                        if the given number is smaller then 4, the application will use 4 threads.
     *                       
     * @param maxTokenLength the maximum length of
     *                       {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.Token}s to be created by
     *                       the {@link TokenFactory}. The minimum length is <code>0</code>, the maximum length
     *                       (and default value) is <code>8</code>. This can be used to limit the amount of parallel
     *                       message exchanges with one server (see {@link TokenFactory} for details).
     */
    public CoapClientApplication(String name, int port, int numberOfThreads, int maxTokenLength){

        this.name = name;

        if(maxTokenLength < 0 || maxTokenLength > 8)
            throw new IllegalArgumentException("Token length must be between 0 and 8 (both inclusive)");

        int threads = Math.max(numberOfThreads, 4);

        ThreadFactory threadFactory =
                new ThreadFactoryBuilder().setNameFormat(name + " I/O worker #%d").build();

        ThreadRenamingRunnable.setThreadNameDeterminer(new ThreadNameDeterminer() {
            @Override
            public String determineThreadName(String currentThreadName, String proposedThreadName) throws Exception {
                return null;
            }
        });

        this.exeutor = new ScheduledThreadPoolExecutor(threads, threadFactory);
        this.exeutor.setRemoveOnCancelPolicy(true);

        TokenFactory tokenFactory = new TokenFactory(maxTokenLength);

        //Create factories for channel and pipeline
        ChannelFactory channelFactory = new NioDatagramChannelFactory(exeutor, threads/2);
        ClientChannelPipelineFactory clientChannelPipelineFactory =
                new ClientChannelPipelineFactory(exeutor, tokenFactory);

        //Create and configure bootstrap
        ConnectionlessBootstrap bootstrap = new ConnectionlessBootstrap(channelFactory);
        bootstrap.setPipelineFactory(clientChannelPipelineFactory);
        bootstrap.setOption("receiveBufferSizePredictor",
                new FixedReceiveBufferSizePredictor(RECEIVE_BUFFER_SIZE));

        //Create datagram channel
        this.channel = (DatagramChannel) bootstrap.bind(new InetSocketAddress(port));

        //set the outbound reliability handler with its channel handler context
        ChannelPipeline pipeline = this.channel.getPipeline();
        String handlerName = ClientChannelPipelineFactory.OUTBOUND_RELIABILITY_HANDLER;
        ChannelHandlerContext ctx = pipeline.getContext(handlerName);
        ((OutboundReliabilityHandler) pipeline.get(handlerName)).setChannelHandlerContext(ctx);


        log.info("New client channel created for address {}", this.channel.getLocalAddress());
    }

    /**
     * Creates a new instance.
     *
     * Invocation of this constructor has the same effect as {@link #CoapClientApplication(String, int, int, int)} with
     * <code>"CoAP Client"</code> as parameter name.
     *
     * @param port the port, this {@link CoapClientApplication} should be bound to (use <code>0</code> for
     *             arbitrary port)
     * @param numberOfThreads the number of threads to be used for I/O operations. The minimum number is 4, i.e. even
     *                        if the given number is smaller then 4, the application will use 4 threads.
     *
     * @param maxTokenLength the maximum length of
     *                       {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.Token}s to be created by
     *                       the {@link TokenFactory}. The minimum length is <code>0</code>, the maximum length
     *                       (and default value) is <code>8</code>. This can be used to limit the amount of parallel
     *                       message exchanges with one server (see {@link TokenFactory} for details).
     */
    public CoapClientApplication(int port, int numberOfThreads, int maxTokenLength){
        this("CoAP Client", port, numberOfThreads, maxTokenLength);
    }


    /**
     * Creates a new instance of {@link CoapClientApplication} with default parameters.
     *
     * Invocation of this constructor has the same effect as {@link #CoapClientApplication(String, int, int)} with
     * parameters <code>name = "CoAP Client"</code>, <code>port = 0</code>, <code>maxTokenLength = 8</code>.
     */
    public CoapClientApplication(){
        this("CoAP Client", 0, 8);
    }

    /**
     * Creates a new instance of {@link CoapClientApplication}.
     *
     * Invocation of this constructor has the same effect as {@link #CoapClientApplication(String, int, int)} with
     * parameters <code>name = name</code>, <code>port = 0</code>, <code>maxTokenLength = 8</code>.
     */
    public CoapClientApplication(String name){
        this(name, 0, 8);
    }

    /**
     * Creates a new instance of {@link CoapClientApplication}.
     *
     * Invocation of this constructor has the same effect as {@link #CoapClientApplication(String, int, int, int)} with
     * parameters <code>name = name</code>, <code>port = 0</code>, <code>maxTokenLength = 8</code>, and
     * <code>numberOfThreads = Runtime.getRuntime().availableProcessors() * 2)</code>.
     *
     * @param name the name of the application (used for logging purposes)
     * @param port the port, this {@link CoapClientApplication} should be bound to (use <code>0</code> for
     *             arbitrary port)
     * @param maxTokenLength the maximum length of
     *                       {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.Token}s to be created by
     *                       the {@link TokenFactory}. The minimum length is <code>0</code>, the maximum length
     *                       (and default value) is <code>8</code>. This can be used to limit the amount of parallel
     *                       message exchanges with one server (see {@link TokenFactory} for details).
     */
    public CoapClientApplication(String name, int port, int maxTokenLength){
        this(name, port, Runtime.getRuntime().availableProcessors() * 2, maxTokenLength);
    }


    /**
     * Sends a {@link de.uniluebeck.itm.ncoap.message.CoapRequest} to the given remote endpoints, i.e. CoAP server or
     * proxy, and registers the given {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback}
     * to be called upon reception of a {@link de.uniluebeck.itm.ncoap.message.CoapResponse}.
     *
     * <b>Note:</b> Override {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback
     * #continueObservation(InetSocketAddress, Token)} on the given callback for observations!
     *
     * @param coapRequest the {@link de.uniluebeck.itm.ncoap.message.CoapRequest} to be sent
     *
     * @param clientCallback the {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback} to process the corresponding response, resp.
     *                              update notification (which are also instances of {@link CoapResponse}.
     *
     * @param remoteEndpoint the desired recipient of the given {@link de.uniluebeck.itm.ncoap.message.CoapRequest}
     */
    public void sendCoapRequest(final CoapRequest coapRequest, final ClientCallback clientCallback,
                                final InetSocketAddress remoteEndpoint){

        exeutor.submit(new Runnable() {

            @Override
            public void run() {
                OutboundMessageWrapper message = new OutboundMessageWrapper(coapRequest, clientCallback);

                ChannelFuture future = Channels.write(channel, message, remoteEndpoint);
                future.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (future.isSuccess()) {
                            log.debug("Sent to {}:{}: {}",
                                    new Object[]{remoteEndpoint.getAddress().getHostAddress(),
                                            remoteEndpoint.getPort(), coapRequest});
                        }
                    }
                });
            }

        });
    }


    /**
     * Sends a CoAP PING, i.e. a {@link de.uniluebeck.itm.ncoap.message.CoapMessage} with
     * {@link de.uniluebeck.itm.ncoap.message.MessageType.Name#CON} and
     * {@link de.uniluebeck.itm.ncoap.message.MessageCode.Name#EMPTY} to the given CoAP endpoints and registers the
     * given {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback}
     * to be called upon reception of the corresponding {@link de.uniluebeck.itm.ncoap.message.MessageType.Name#RST}
     * message (CoAP PONG).
     *
     * Make sure to override {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback
     * #processReset()} to handle the CoAP PONG!
     *
     * @param clientCallback the {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback} to be
     *                       called upon reception of the corresponding
     *                       {@link de.uniluebeck.itm.ncoap.message.MessageType.Name#RST} message.
     *                       <br><br>
     *                       <b>Note:</b> To handle the CoAP PONG, i.e. the empty RST, the method
     *                       {@link de.uniluebeck.itm.ncoap.communication.dispatching.client.ClientCallback
     *                       #processReset()} MUST be overridden
     * @param remoteEndpoint the desired recipient of the CoAP PING message
     */
    public void sendCoapPing(final ClientCallback clientCallback, final InetSocketAddress remoteEndpoint){

        exeutor.submit(new Runnable() {

            @Override
            public void run() {

                final CoapMessage coapPing = CoapMessage.createPing(CoapMessage.UNDEFINED_MESSAGE_ID);
                OutboundMessageWrapper wrapper = new OutboundMessageWrapper(coapPing, clientCallback);

                ChannelFuture future = Channels.write(channel, wrapper, remoteEndpoint);
                future.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            Throwable cause = future.getCause();
                            log.error("Error with CoAP ping!", cause);
                            String description = cause == null ? "UNEXPECTED ERROR!" : cause.getMessage();
                            clientCallback.processMiscellaneousError(description);
                        }
                    }
                });
            }
        });

    }


    /**
     * Returns the local port number the {@link org.jboss.netty.channel.socket.DatagramChannel} of this
     * {@link de.uniluebeck.itm.ncoap.application.client.CoapClientApplication} is bound to.
     *
     * @return the local port number the {@link org.jboss.netty.channel.socket.DatagramChannel} of this
     * {@link de.uniluebeck.itm.ncoap.application.client.CoapClientApplication} is bound to.
     */
    public int getPort() {
        return this.channel.getLocalAddress().getPort();
    }

    /**
     * Shuts this {@link de.uniluebeck.itm.ncoap.application.client.CoapClientApplication} down by closing its
     * {@link org.jboss.netty.channel.socket.DatagramChannel} which includes to unbind
     * this {@link org.jboss.netty.channel.socket.DatagramChannel} from the listening port and by this means free the
     * port.
     */
    public final void shutdown(){
        log.warn("Start to shutdown " + this.getName() + " (Port : " + this.getPort() + ")");

        this.channel.close().awaitUninterruptibly().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                log.warn("Channel closed ({}).", CoapClientApplication.this.getName());
                channel.getFactory().releaseExternalResources();
                log.warn("External resources released ({}).", CoapClientApplication.this.getName());
                log.warn("Shutdown of " + CoapClientApplication.this.name + " completed.");
            }
        });
    }

    /**
     * Returns the name of this {@link de.uniluebeck.itm.ncoap.application.client.CoapClientApplication} instance
     *
     * @return the name of this {@link de.uniluebeck.itm.ncoap.application.client.CoapClientApplication} instance
     */
    public String getName() {
        return name;
    }
}
TOP

Related Classes of de.uniluebeck.itm.ncoap.application.client.CoapClientApplication

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.