/*
* Copyright 2014-2016 CyberVision, Inc.
*
* 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 org.kaaproject.kaa.client.channel.impl.transports;
import org.kaaproject.kaa.client.channel.NotificationTransport;
import org.kaaproject.kaa.client.notification.NotificationProcessor;
import org.kaaproject.kaa.client.notification.TopicListHashCalculator;
import org.kaaproject.kaa.common.TransportType;
import org.kaaproject.kaa.common.endpoint.gen.Notification;
import org.kaaproject.kaa.common.endpoint.gen.NotificationSyncRequest;
import org.kaaproject.kaa.common.endpoint.gen.NotificationSyncResponse;
import org.kaaproject.kaa.common.endpoint.gen.SubscriptionCommand;
import org.kaaproject.kaa.common.endpoint.gen.SubscriptionCommandType;
import org.kaaproject.kaa.common.endpoint.gen.SyncResponseStatus;
import org.kaaproject.kaa.common.endpoint.gen.Topic;
import org.kaaproject.kaa.common.endpoint.gen.TopicState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class DefaultNotificationTransport extends AbstractKaaTransport
implements NotificationTransport {
private static final Logger LOG = LoggerFactory.getLogger(DefaultNotificationTransport.class);
private final Set<String> acceptedUnicastNotificationIds = new HashSet<>();
private final List<SubscriptionCommand> sentNotificationCommands = new LinkedList<>();
private NotificationProcessor processor;
private List<TopicState> getTopicStates() {
List<TopicState> states = null;
Map<Long, Integer> nfSubscriptions = clientState.getNfSubscriptions();
if (!nfSubscriptions.isEmpty()) {
states = new ArrayList<>();
LOG.info("Topic States:");
for (Entry<Long, Integer> nfSubscription : nfSubscriptions.entrySet()) {
TopicState state = new TopicState(nfSubscription.getKey(), nfSubscription.getValue());
states.add(state);
LOG.info("{} : {}", state.getTopicId(), state.getSeqNumber());
}
}
return states;
}
@Override
public NotificationSyncRequest createEmptyNotificationRequest() {
if (clientState != null) {
NotificationSyncRequest request = new NotificationSyncRequest();
request.setTopicListHash(clientState.getTopicListHash());
request.setTopicStates(getTopicStates());
return request;
}
return null;
}
@Override
public NotificationSyncRequest createNotificationRequest() {
if (clientState != null) {
NotificationSyncRequest request = new NotificationSyncRequest();
if (!acceptedUnicastNotificationIds.isEmpty()) {
LOG.info("Accepted unicast Notifications: {}", acceptedUnicastNotificationIds.size());
request.setAcceptedUnicastNotifications(new ArrayList<>(acceptedUnicastNotificationIds));
}
request.setSubscriptionCommands(sentNotificationCommands);
request.setTopicListHash(clientState.getTopicListHash());
request.setTopicStates(getTopicStates());
return request;
}
return null;
}
@Override
public void onNotificationResponse(NotificationSyncResponse response) throws IOException {
if (processor != null && clientState != null) {
if (response.getResponseStatus() == SyncResponseStatus.NO_DELTA) {
acceptedUnicastNotificationIds.clear();
} else {
List<Topic> topics = response.getAvailableTopics();
if (topics != null) {
clientState.setTopicListHash(TopicListHashCalculator.calculateTopicListHash(topics));
processor.topicsListUpdated(topics);
}
}
for (SubscriptionCommand subscriptionCommand : sentNotificationCommands) {
if (subscriptionCommand.getCommand() == SubscriptionCommandType.ADD) {
clientState.addTopicSubscription(subscriptionCommand.getTopicId());
} else if (subscriptionCommand.getCommand() == SubscriptionCommandType.REMOVE) {
clientState.removeTopicSubscription(subscriptionCommand.getTopicId());
}
}
List<Notification> notifications = response.getNotifications();
if (notifications != null) {
List<Notification> newNotifications = new ArrayList<>(notifications.size());
List<Notification> unicastNotifications = getUnicastNotifications(notifications);
List<Notification> multicastNotifications = getMulticastNotifications(notifications);
for (Notification notification : unicastNotifications) {
LOG.info("Received {}", notification);
if (acceptedUnicastNotificationIds.add(notification.getUid())) {
newNotifications.add(notification);
} else {
LOG.info("Notification with uid [{}] was already received", notification.getUid());
}
}
for (Notification notification : multicastNotifications) {
LOG.info("Received {}", notification);
if (clientState.updateTopicSubscriptionInfo(notification.getTopicId(),
notification.getSeqNumber())) {
newNotifications.add(notification);
} else {
LOG.info("Notification with seq number {} was already received",
notification.getSeqNumber());
}
}
processor.notificationReceived(newNotifications);
}
sentNotificationCommands.clear();
syncAck(response.getResponseStatus());
LOG.info("Processed notification response.");
}
}
@Override
public void onSubscriptionChanged(List<SubscriptionCommand> commands) {
synchronized (sentNotificationCommands) {
sentNotificationCommands.addAll(commands);
}
}
private List<Notification> getUnicastNotifications(List<Notification> notifications) {
List<Notification> result = new ArrayList<>();
for (Notification notification : notifications) {
if (notification.getUid() != null) {
result.add(notification);
}
}
return result;
}
private List<Notification> getMulticastNotifications(List<Notification> notifications) {
List<Notification> result = new ArrayList<>();
for (Notification notification : notifications) {
if (notification.getUid() == null) {
result.add(notification);
}
}
Collections.sort(result, new Comparator<Notification>() {
@Override
public int compare(Notification o1, Notification o2) {
return o1.getSeqNumber() - o2.getSeqNumber();
}
});
return result;
}
@Override
public void setNotificationProcessor(NotificationProcessor processor) {
this.processor = processor;
}
@Override
protected TransportType getTransportType() {
return TransportType.NOTIFICATION;
}
}