com.devicehive.websockets.handlers.NotificationHandlers.java Source code

Java tutorial

Introduction

Here is the source code for com.devicehive.websockets.handlers.NotificationHandlers.java

Source

package com.devicehive.websockets.handlers;

/*
 * #%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.auth.HivePrincipal;
import com.devicehive.configuration.Constants;
import com.devicehive.configuration.Messages;
import com.devicehive.exceptions.HiveException;
import com.devicehive.model.DeviceNotification;
import com.devicehive.model.websockets.InsertNotification;
import com.devicehive.model.wrappers.DeviceNotificationWrapper;
import com.devicehive.resource.util.JsonTypes;
import com.devicehive.service.DeviceNotificationService;
import com.devicehive.service.DeviceService;
import com.devicehive.util.ServerResponsesFactory;
import com.devicehive.vo.DeviceVO;
import com.devicehive.websockets.converters.WebSocketResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.commons.lang3.StringUtils;
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.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

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

import static com.devicehive.configuration.Constants.*;
import static com.devicehive.json.strategies.JsonPolicyDef.Policy.NOTIFICATION_TO_DEVICE;
import static com.devicehive.messages.handler.WebSocketClientHandler.sendMessage;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

@Component
public class NotificationHandlers {
    private static final Logger logger = LoggerFactory.getLogger(NotificationHandlers.class);

    public static final String SUBSCSRIPTION_SET_NAME = "notificationSubscriptions";

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private DeviceNotificationService notificationService;

    @Autowired
    private Gson gson;

    @PreAuthorize("isAuthenticated() and hasPermission(null, 'GET_DEVICE_NOTIFICATION')")
    public WebSocketResponse processNotificationSubscribe(JsonObject request, WebSocketSession session)
            throws InterruptedException {
        HivePrincipal principal = (HivePrincipal) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();
        Date timestamp = gson.fromJson(request.get(Constants.TIMESTAMP), Date.class);
        Set<String> devices = gson.fromJson(request.get(Constants.DEVICE_GUIDS), JsonTypes.STRING_SET_TYPE);
        Set<String> names = gson.fromJson(request.get(Constants.NAMES), JsonTypes.STRING_SET_TYPE);
        String deviceId = Optional.ofNullable(request.get(Constants.DEVICE_GUID)).map(JsonElement::getAsString)
                .orElse(null);

        logger.debug("notification/subscribe requested for devices: {}, {}. Timestamp: {}. Names {} Session: {}",
                devices, deviceId, timestamp, names, session.getId());

        devices = prepareActualList(devices, deviceId);

        List<DeviceVO> actualDevices;
        if (devices != null) {
            actualDevices = deviceService.findByGuidWithPermissionsCheck(devices, principal);
            if (actualDevices.size() != devices.size()) {
                throw new HiveException(String.format(Messages.DEVICES_NOT_FOUND, devices), SC_FORBIDDEN);
            }
        } else {
            actualDevices = deviceService
                    .list(null, null, null, null, null, null, null, true, null, null, principal).join();
            devices = actualDevices.stream().map(DeviceVO::getGuid).collect(Collectors.toSet());
        }

        BiConsumer<DeviceNotification, String> callback = (notification, subscriptionId) -> {
            JsonObject json = ServerResponsesFactory.createNotificationInsertMessage(notification, subscriptionId);
            sendMessage(json, session);
        };

        Pair<String, CompletableFuture<List<DeviceNotification>>> pair = notificationService.subscribe(devices,
                names, timestamp, callback);

        pair.getRight().thenAccept(collection -> collection.forEach(notification -> {
            JsonObject json = ServerResponsesFactory.createNotificationInsertMessage(notification, pair.getLeft());
            sendMessage(json, session);
        }));

        logger.debug("notification/subscribe done for devices: {}, {}. Timestamp: {}. Names {} Session: {}",
                devices, deviceId, timestamp, names, session.getId());

        ((CopyOnWriteArraySet) session.getAttributes().get(SUBSCSRIPTION_SET_NAME)).add(pair.getLeft());

        WebSocketResponse response = new WebSocketResponse();
        response.addValue(SUBSCRIPTION_ID, pair.getLeft(), null);
        return response;
    }

    /**
     * Implementation of the <a href="http://www.devicehive.com/restful#WsReference/Client/notificationunsubscribe">
     * WebSocket API: Client: notification/unsubscribe</a> Unsubscribes from device notifications.
     *
     * @param session Current session
     * @return Json object with the following structure <code> { "action": {string}, "status": {string}, "requestId":
     * {object} } </code>
     */
    @PreAuthorize("isAuthenticated() and hasPermission(null, 'GET_DEVICE_NOTIFICATION')")
    public WebSocketResponse processNotificationUnsubscribe(JsonObject request, WebSocketSession session) {
        HivePrincipal principal = (HivePrincipal) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();
        Optional<String> subId = Optional.ofNullable(request.get(SUBSCRIPTION_ID)).map(s -> {
            try {
                return s.getAsString();
            } catch (UnsupportedOperationException e) {
                logger.error("Subscription Id is null");
                return StringUtils.EMPTY;
            }
        });
        Set<String> deviceGuids = gson.fromJson(request.get(DEVICE_GUIDS), JsonTypes.STRING_SET_TYPE);
        logger.debug("notification/unsubscribe action. Session {} ", session.getId());
        if (!subId.isPresent() && deviceGuids == null) {
            List<DeviceVO> actualDevices = deviceService
                    .list(null, null, null, null, null, null, null, true, null, null, principal).join();
            deviceGuids = actualDevices.stream().map(DeviceVO::getGuid).collect(Collectors.toSet());
            notificationService.unsubscribe(null, deviceGuids);
        } else if (subId.isPresent()) {
            notificationService.unsubscribe(subId.get(), deviceGuids);
        } else {
            notificationService.unsubscribe(null, deviceGuids);
        }
        logger.debug("notification/unsubscribe completed for session {}", session.getId());

        ((CopyOnWriteArraySet) session.getAttributes().get(SUBSCSRIPTION_SET_NAME)).remove(subId);
        return new WebSocketResponse();
    }

    @PreAuthorize("isAuthenticated() and hasPermission(null, 'CREATE_DEVICE_NOTIFICATION')")
    public WebSocketResponse processNotificationInsert(JsonObject request, WebSocketSession session) {
        HivePrincipal principal = (HivePrincipal) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();
        final String deviceGuid = Optional.ofNullable(request.get(Constants.DEVICE_GUID))
                .map(JsonElement::getAsString).orElse(null);
        DeviceNotificationWrapper notificationSubmit = gson.fromJson(request.get(Constants.NOTIFICATION),
                DeviceNotificationWrapper.class);

        logger.debug("notification/insert requested. Session {}. Guid {}", session, deviceGuid);
        if (notificationSubmit == null || notificationSubmit.getNotification() == null) {
            logger.debug("notification/insert proceed with error. Bad notification: notification is required.");
            throw new HiveException(Messages.NOTIFICATION_REQUIRED, SC_BAD_REQUEST);
        }

        Set<DeviceVO> devices = new HashSet<>();
        if (deviceGuid == null) {
            for (String guid : principal.getDeviceGuids()) {
                devices.add(deviceService.findByGuidWithPermissionsCheck(guid, principal));
            }
        } else {
            devices.add(deviceService.findByGuidWithPermissionsCheck(deviceGuid, principal));
        }
        if (devices.isEmpty() || StreamSupport.stream(devices.spliterator(), true).allMatch(o -> o == null)) {
            logger.debug("notification/insert canceled for session: {}. Guid is not provided", session);
            throw new HiveException(Messages.DEVICE_GUID_REQUIRED, SC_FORBIDDEN);
        }

        WebSocketResponse response = new WebSocketResponse();

        for (DeviceVO device : devices) {
            if (device.getNetwork() == null) {
                logger.debug("notification/insert. No network specified for device with guid = {}", deviceGuid);
                throw new HiveException(String.format(Messages.DEVICE_IS_NOT_CONNECTED_TO_NETWORK, deviceGuid),
                        SC_FORBIDDEN);
            }
            DeviceNotification message = notificationService.convertWrapperToNotification(notificationSubmit,
                    device);

            notificationService.insert(message, device).thenApply(notification -> {
                logger.debug("notification/insert proceed successfully. Session {}. Guid {}", session, deviceGuid);
                response.addValue(NOTIFICATION, new InsertNotification(message.getId(), message.getTimestamp()),
                        NOTIFICATION_TO_DEVICE);
                return response;
            }).exceptionally(ex -> {
                logger.warn("Unable to insert notification.", ex);
                throw new HiveException(Messages.INTERNAL_SERVER_ERROR, SC_INTERNAL_SERVER_ERROR);
            }).join();
        }

        return response;
    }

    private Set<String> prepareActualList(Set<String> deviceIdSet, final String deviceId) {
        if (deviceId == null && deviceIdSet == null) {
            return null;
        }
        if (deviceIdSet != null && deviceId == null) {
            deviceIdSet.remove(null);
            return deviceIdSet;
        }

        if (deviceIdSet == null) {
            return new HashSet<String>() {
                {
                    add(deviceId);
                }

                private static final long serialVersionUID = 955343867580964077L;
            };

        }
        throw new HiveException(Messages.INVALID_REQUEST_PARAMETERS, SC_BAD_REQUEST);
    }
}