/* 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 net.maritimecloud.mms.server.broadcast; import static java.util.Objects.requireNonNull; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import net.maritimecloud.internal.mms.messages.PositionReport; import net.maritimecloud.internal.net.messages.Broadcast; import net.maritimecloud.internal.net.messages.BroadcastAck; import net.maritimecloud.mms.server.MmsServerConnectionBus; import net.maritimecloud.mms.server.connection.client.Client; import net.maritimecloud.mms.server.connection.client.ClientManager; import net.maritimecloud.util.geometry.Area; import net.maritimecloud.util.geometry.PositionTime; import org.cakeframework.container.concurrent.ThreadManager; /** * The server side broadcast manager. * * @author Kasper Nielsen */ public class ServerBroadcastManager { final ConcurrentHashMap<String, BroadcastSubscriptionSet> listeners = new ConcurrentHashMap<>(); private final ClientManager tm; private final ThreadManager threadManager; public ServerBroadcastManager(ThreadManager threadManager, ClientManager tm, MmsServerConnectionBus bus) { this.tm = requireNonNull(tm); this.threadManager = threadManager; bus.setBroadcastManager(this); } public PositionReport broadcast(Client sender, Broadcast broadcast) { // final PositionTime sourcePositionTime = send.getPositionTime(); tm.forEachTarget(t -> { // We could do some checks with regards to not send to terminated if (t != sender/* && t.isConnected() */) { // do not broadcast to self threadManager.getExecutor("mms.broadcast").execute(() -> broadcast(sender, broadcast, t)); } }); return new PositionReport(); } void broadcast(Client source, Broadcast broadcast, Client destination) { PositionTime latest = destination.getLatestPositionAndTime(); if (latest != null) { boolean doSend = false; Area area = broadcast.getArea(); // if (area instanceof RelativeCircularArea) { // double distance = sourcePositionTime.geodesicDistanceTo(latest); // RelativeCircularArea c = (RelativeCircularArea) area; // doSend = distance < c.getRadius(); // } else { doSend = area.contains(latest); // } if (doSend) { broadcastSend(source, broadcast, destination); } } } void broadcastSend(Client source, Broadcast broadcast, Client destination) { Broadcast bd = new Broadcast(); bd.setMessageId(broadcast.getMessageId()); bd.setBroadcastType(broadcast.getBroadcastType()); bd.setSenderId(broadcast.getSenderId()); bd.setSenderTimestamp(broadcast.getSenderTimestamp()); bd.setSenderPosition(broadcast.getSenderPosition()); bd.setPayload(broadcast.getPayload()); bd.setSignature(broadcast.getSignature()); CompletableFuture<Void> acked = destination.send(bd).protocolAcked(); if (broadcast.hasAckBroadcast()) { acked.thenAccept(e -> { BroadcastAck ba = new BroadcastAck(); ba.setAckForMessageId(bd.getMessageId()); // Ignore original sender id ba.setReceiverId(destination.getId()); PositionTime pt = destination.getLatestPositionAndTime(); ba.setReceiverTimestamp(pt.timestamp()); ba.setReceiverPosition(pt); source.send(ba); }); } } }