/* Copyright (c) 2011 Danish Maritime Authority.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dk.dma.ais.reader;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Charsets;
import dk.dma.ais.sentence.Abk;
import java.util.function.Consumer;
/**
* Thread class for reading AIS messages from UDP
*
*/
public class AisUdpReader extends AisReader {
private static final Logger LOG = LoggerFactory.getLogger(AisUdpReader.class);
private final InetSocketAddress addr;
private volatile DatagramSocket socket;
private volatile Thread reader;
AisUdpReader(int port) {
this(null, port);
}
AisUdpReader(String address, int port) {
addr = (address == null) ? new InetSocketAddress(port) : new InetSocketAddress(address, port);
}
/**
* Main read loop
*/
@Override
public void run() {
try {
final UdpSentenceInputStream stream = new UdpSentenceInputStream();
socket = new DatagramSocket(addr);
reader = new Thread() {
@Override
public void run() {
final byte[] receiveData = new byte[256];
while (true) {
try {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
String sentence = new String(receivePacket.getData(), 0, receivePacket.getLength(), Charsets.US_ASCII);
stream.push(sentence.getBytes(StandardCharsets.US_ASCII));
} catch (IOException | InterruptedException e) {
if (isShutdown() || isInterrupted()) {
return;
}
LOG.error("Failed to read datagrams", e);
return;
}
}
}
};
reader.start();
readLoop(stream);
} catch (IOException e) {
if (isShutdown() || isInterrupted()) {
return;
}
LOG.error("Failed to listen for datagrams", e);
}
}
@Override
public void stopReader() {
super.stopReader();
socket.close();
reader.interrupt();
}
@Override
public void send(SendRequest sendRequest, Consumer<Abk> resultListener) throws SendException {
throw new UnsupportedOperationException();
}
public Status getStatus() {
return (socket != null) ? Status.CONNECTED : Status.DISCONNECTED;
}
public String toString() {
return "AisUdpReader [sourceId = " + getSourceId() + "]";
}
private class UdpSentenceInputStream extends InputStream {
private final ArrayBlockingQueue<byte[]> queue;
public UdpSentenceInputStream() {
this(1024);
}
public UdpSentenceInputStream(int maxLines) {
queue = new ArrayBlockingQueue<>(maxLines);
}
public void push(byte[] packet) throws InterruptedException {
queue.put(packet);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
byte[] packet;
try {
packet = queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 0;
}
if (len < packet.length) {
throw new IOException("Read with too small buffer");
}
System.arraycopy(packet, 0, b, off, packet.length);
return packet.length;
}
@Override
public int read() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int read(byte[] b) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long skip(long n) throws IOException {
throw new UnsupportedOperationException();
}
}
}