com.devicehive.service.DeviceNotificationService.java Source code

Java tutorial

Introduction

Here is the source code for com.devicehive.service.DeviceNotificationService.java

Source

package com.devicehive.service;

/*
 * #%L
 * DeviceHive Frontend Logic
 * %%
 * Copyright (C) 2016 DataArt
 * %%
 * 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.
 * #L%
 */

import com.devicehive.dao.DeviceDao;
import com.devicehive.model.DeviceNotification;
import com.devicehive.model.SpecialNotifications;
import com.devicehive.model.eventbus.events.NotificationEvent;
import com.devicehive.model.rpc.*;
import com.devicehive.model.wrappers.DeviceNotificationWrapper;
import com.devicehive.service.helpers.ResponseConsumer;
import com.devicehive.service.time.TimestampService;
import com.devicehive.shim.api.Request;
import com.devicehive.shim.api.Response;
import com.devicehive.shim.api.client.RpcClient;
import com.devicehive.util.ServerResponsesFactory;
import com.devicehive.vo.DeviceVO;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class DeviceNotificationService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceNotificationService.class);

    private DeviceEquipmentService deviceEquipmentService;
    private TimestampService timestampService;
    private DeviceDao deviceDao;
    private RpcClient rpcClient;

    @Autowired
    public DeviceNotificationService(DeviceEquipmentService deviceEquipmentService,
            TimestampService timestampService, DeviceDao deviceDao, RpcClient rpcClient) {
        this.deviceEquipmentService = deviceEquipmentService;
        this.timestampService = timestampService;
        this.deviceDao = deviceDao;
        this.rpcClient = rpcClient;
    }

    public CompletableFuture<Optional<DeviceNotification>> findOne(Long id, String guid) {
        NotificationSearchRequest searchRequest = new NotificationSearchRequest();
        searchRequest.setId(id);
        searchRequest.setGuid(guid);

        CompletableFuture<Response> future = new CompletableFuture<>();
        rpcClient.call(
                Request.newBuilder().withBody(searchRequest).withPartitionKey(searchRequest.getGuid()).build(),
                new ResponseConsumer(future));
        return future
                .thenApply(r -> ((NotificationSearchResponse) r.getBody()).getNotifications().stream().findFirst());
    }

    @SuppressWarnings("unchecked")
    public CompletableFuture<List<DeviceNotification>> find(Set<String> guids, Set<String> names, Date timestampSt,
            Date timestampEnd) {
        List<CompletableFuture<Response>> futures = guids.stream().map(guid -> {
            NotificationSearchRequest searchRequest = new NotificationSearchRequest();
            searchRequest.setGuid(guid);
            searchRequest.setNames(names);
            searchRequest.setTimestampStart(timestampSt);
            searchRequest.setTimestampEnd(timestampEnd);
            return searchRequest;
        }).map(searchRequest -> {
            CompletableFuture<Response> future = new CompletableFuture<>();
            rpcClient.call(
                    Request.newBuilder().withBody(searchRequest).withPartitionKey(searchRequest.getGuid()).build(),
                    new ResponseConsumer(future));
            return future;
        }).collect(Collectors.toList());

        // List<CompletableFuture<Response>> => CompletableFuture<List<DeviceNotification>>
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
                .thenApply(v -> futures.stream().map(CompletableFuture::join) // List<CompletableFuture<Response>> => CompletableFuture<List<Response>>
                        .map(r -> r.getBody().cast(NotificationSearchResponse.class).getNotifications()) // CompletableFuture<List<Response>> => CompletableFuture<List<List<DeviceNotification>>>
                        .flatMap(Collection::stream) // CompletableFuture<List<List<DeviceNotification>>> => CompletableFuture<List<DeviceNotification>>
                        .collect(Collectors.toList()));
    }

    public CompletableFuture<DeviceNotification> insert(final DeviceNotification notification,
            final DeviceVO device) {
        List<CompletableFuture<Response>> futures = processDeviceNotification(notification, device).stream()
                .map(n -> {
                    CompletableFuture<Response> future = new CompletableFuture<>();
                    rpcClient.call(Request.newBuilder().withBody(new NotificationInsertRequest(n))
                            .withPartitionKey(device.getGuid()).build(), new ResponseConsumer(future));
                    return future;
                }).collect(Collectors.toList());

        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
                .thenApply(x -> futures.stream().map(CompletableFuture::join)
                        .map(r -> r.getBody().cast(NotificationInsertResponse.class).getDeviceNotification())
                        .filter(n -> !SpecialNotifications.DEVICE_UPDATE.equals(n.getNotification())) // we are not going to return DEVICE_UPDATE notification
                        .collect(Collectors.toList()).get(0)); // after filter we should get only one notification
    }

    public Pair<String, CompletableFuture<List<DeviceNotification>>> subscribe(final Set<String> devices,
            final Set<String> names, final Date timestamp, final BiConsumer<DeviceNotification, String> callback) {

        final String subscriptionId = UUID.randomUUID().toString();
        Set<NotificationSubscribeRequest> subscribeRequests = devices.stream()
                .map(device -> new NotificationSubscribeRequest(subscriptionId, device, names, timestamp))
                .collect(Collectors.toSet());
        Collection<CompletableFuture<Collection<DeviceNotification>>> futures = new ArrayList<>();
        for (NotificationSubscribeRequest sr : subscribeRequests) {
            CompletableFuture<Collection<DeviceNotification>> future = new CompletableFuture<>();
            Consumer<Response> responseConsumer = response -> {
                String resAction = response.getBody().getAction();
                if (resAction.equals(Action.NOTIFICATION_SUBSCRIBE_RESPONSE.name())) {
                    NotificationSubscribeResponse r = response.getBody().cast(NotificationSubscribeResponse.class);
                    future.complete(r.getNotifications());
                } else if (resAction.equals(Action.NOTIFICATION_EVENT.name())) {
                    NotificationEvent event = response.getBody().cast(NotificationEvent.class);
                    callback.accept(event.getNotification(), subscriptionId);
                } else {
                    logger.warn("Unknown action received from backend {}", resAction);
                }
            };
            futures.add(future);
            Request request = Request.newBuilder().withBody(sr).withPartitionKey(sr.getDevice())
                    .withSingleReply(false).build();
            rpcClient.call(request, responseConsumer);
        }

        CompletableFuture<List<DeviceNotification>> future = CompletableFuture
                .allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> futures.stream()
                        .map(CompletableFuture::join).flatMap(Collection::stream).collect(Collectors.toList()));
        return Pair.of(subscriptionId, future);
    }

    public void unsubscribe(String subId, Set<String> deviceGuids) {
        NotificationUnsubscribeRequest unsubscribeRequest = new NotificationUnsubscribeRequest(subId, deviceGuids);
        Request request = Request.newBuilder().withBody(unsubscribeRequest).build();
        rpcClient.push(request);
    }

    public DeviceNotification convertWrapperToNotification(DeviceNotificationWrapper notificationSubmit,
            DeviceVO device) {
        DeviceNotification notification = new DeviceNotification();
        notification.setId(Math.abs(new Random().nextInt()));
        notification.setDeviceGuid(device.getGuid());
        if (notificationSubmit.getTimestamp() == null) {
            notification.setTimestamp(timestampService.getDate());
        } else {
            notification.setTimestamp(notificationSubmit.getTimestamp());
        }
        notification.setNotification(notificationSubmit.getNotification());
        notification.setParameters(notificationSubmit.getParameters());
        return notification;
    }

    private List<DeviceNotification> processDeviceNotification(DeviceNotification notificationMessage,
            DeviceVO device) {
        List<DeviceNotification> notificationsToCreate = new ArrayList<>();
        if (notificationMessage.getNotification() != null
                && notificationMessage.getNotification().equals(SpecialNotifications.EQUIPMENT)) {
            deviceEquipmentService.refreshDeviceEquipment(notificationMessage, device);
        }

        notificationsToCreate.add(notificationMessage);
        return notificationsToCreate;
    }
}