/* 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 static java.util.Objects.requireNonNull;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.AbstractIdleService;
import com.google.common.util.concurrent.Service;
import dk.dma.ais.packet.AisPacket;
import dk.dma.ais.packet.AisPacketStream;
/**
* A reader group organizes a group of readers.
*
* @author Kasper Nielsen
*/
public class AisReaderGroup implements Iterable<AisReader> {
/** The logger. */
static final Logger LOG = LoggerFactory.getLogger(AisReaderGroup.class);
/** A stream of all incoming packets. */
final AisPacketStream stream = AisPacketStream.newStream();
final ReentrantLock lock = new ReentrantLock();
/** All readers configured for this group. */
final ConcurrentHashMap<String, AisTcpReader> readers = new ConcurrentHashMap<>();
/** All current subscriptions. */
final ConcurrentHashMap<AisReader, AisPacketStream.Subscription> subscriptions = new ConcurrentHashMap<>();
/** The name of the group. */
final String name;
AisReaderGroup(String name) {
this.name = requireNonNull(name);
}
void add(AisTcpReader reader) {
lock.lock();
try {
if (readers.containsKey(reader.getSourceId())) {
throw new IllegalArgumentException("A reader with the specified source has already been added, id = "
+ reader.getSourceId());
}
readers.put(reader.getSourceId(), reader);
subscriptions.put(reader, reader.stream().subscribe(new Consumer<AisPacket>() {
public void accept(AisPacket p) {
stream.add(p);
}
}));
} finally {
lock.unlock();
}
}
public Service asService() {
return new AbstractIdleService() {
@Override
protected void shutDown() throws Exception {
lock.lock();
try {
for (AisReader r : readers.values()) {
LOG.info("Trying to stop reader " + r);
r.stopReader();
}
for (AisReader r : readers.values()) {
try {
LOG.info("Trying to join reader thread" + r);
r.join();
} catch (InterruptedException e) {
LOG.error("Interrupted while waiting for shutdown", e);
}
}
} finally {
lock.unlock();
}
}
@Override
protected void startUp() throws Exception {
lock.lock();
try {
for (AisReader r : readers.values()) {
r.start();
}
} finally {
lock.unlock();
}
}
};
}
/** {@inheritDoc} */
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Iterator<AisReader> iterator() {
return (Iterator) Collections.unmodifiableCollection(readers.values()).iterator();
}
boolean remove(String name) {
lock.lock();
try {
AisReader reader = readers.get(name);
if (reader != null) {
subscriptions.remove(reader).cancel();
}
reader.stopReader();
return reader != null;
} finally {
lock.unlock();
}
}
/**
* Returns a stream of incoming packets for all the readers this group is managing.
*
* @return a stream of incoming packets for all the readers this group is managing
*/
public AisPacketStream stream() {
return stream.immutableStream();
}
}