/*
* Copyright Ericsson AB 2011-2014. All Rights Reserved.
*
* The contents of this file are subject to the Lesser GNU Public License,
* (the "License"), either version 2.1 of the License, or
* (at your option) any later version.; you may not use this file except in
* compliance with the License. You should have received a copy of the
* License along with this software. If not, it can be
* retrieved online at https://www.gnu.org/licenses/lgpl.html. Moreover
* it could also be requested from Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
* WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
* EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
* OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
* LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE,
* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
*
* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
* WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
* REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
* DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
* DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
* (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
* INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE
* OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
* HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
*/
package com.ericsson.deviceaccess.coap.basedriver.communication;
import com.ericsson.deviceaccess.coap.basedriver.api.message.CoAPMessage;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.MulticastSocket;
import java.net.SocketAddress;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for sending out UDP using either unicast or multicast socket.
*/
public class UDPSender implements TransportLayerSender, Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(UDPSender.class);
private final UDPTask POISON = new UDPTask(null);
private MulticastSocket multicastSocket;
private DatagramSocket socket;
private BlockingQueue<UDPTask> queue;
private volatile AtomicBoolean running = new AtomicBoolean(false);
private Thread thread;
/**
* Constructor with multicast socket used for sending
*
* @param multicastSocket multicast socket to be used for sending
*/
public UDPSender(MulticastSocket multicastSocket) {
this.multicastSocket = multicastSocket;
this.queue = new LinkedBlockingQueue<>();
}
/**
* Constructor with unicast socket used for sending
*
* @param socket socket to be used for sending
*/
public UDPSender(DatagramSocket socket) {
this.socket = socket;
this.queue = new LinkedBlockingQueue<>();
}
@Override
public void start() {
running.set(true);
thread = new Thread(this);
thread.start();
}
@Override
public void run() {
UDPTask task;
while (running.get()) {
try {
task = queue.take();
} catch (InterruptedException ex) {
continue;
}
if (task == POISON) {
break;
}
task.run();
}
if (multicastSocket != null) {
multicastSocket.close();
}
if (socket != null) {
socket.close();
}
queue = null;
thread = null;
}
@Override
public void sendMessage(CoAPMessage message) {
queue.add(new UDPTask(message));
}
/**
* Send given content to the given address
*
* @param content content to be sent
* @param contentLength length of content to be sent
* @param socketAddress address to where the content should be sent
*/
public void send(byte[] content, int contentLength, SocketAddress socketAddress) {
try {
DatagramPacket outgoingDatagram = new DatagramPacket(content, content.length, socketAddress);
if (socket != null) {
socket.send(outgoingDatagram);
} else {
multicastSocket.send(outgoingDatagram);
}
} catch (IOException e) {
LOGGER.warn("Couldn't send content.", e);
}
}
/**
* This method will stop the running threads and close the sockets.
*/
@Override
public void stopService() {
running.set(false);
queue.add(POISON);
}
protected class UDPTask implements Runnable {
private final CoAPMessage message;
protected UDPTask(CoAPMessage message) {
this.message = message;
}
@Override
public void run() {
byte[] encoded = message.encoded();
send(encoded, encoded.length, message.getSocketAddress());
}
}
}