com.devicehive.resource.impl.DeviceNotificationResourceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.devicehive.resource.impl.DeviceNotificationResourceImpl.java

Source

package com.devicehive.resource.impl;

/*
 * #%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.Messages;
import com.devicehive.json.strategies.JsonPolicyDef;
import com.devicehive.model.DeviceNotification;
import com.devicehive.model.ErrorResponse;
import com.devicehive.model.wrappers.DeviceNotificationWrapper;
import com.devicehive.resource.DeviceNotificationResource;
import com.devicehive.resource.converters.TimestampQueryParamParser;
import com.devicehive.resource.util.CommandResponseFilterAndSort;
import com.devicehive.resource.util.ResponseFactory;
import com.devicehive.service.DeviceNotificationService;
import com.devicehive.service.DeviceService;
import com.devicehive.service.time.TimestampService;
import com.devicehive.vo.DeviceVO;
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.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.CompletionCallback;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Response;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

import static javax.ws.rs.core.Response.Status.*;

/**
 * {@inheritDoc}
 */
@Service
public class DeviceNotificationResourceImpl implements DeviceNotificationResource {
    private static final Logger logger = LoggerFactory.getLogger(DeviceNotificationResourceImpl.class);

    @Autowired
    private DeviceNotificationService notificationService;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private TimestampService timestampService;

    /**
     * {@inheritDoc}
     */
    @Override
    public void query(String guid, String startTs, String endTs, String notification, String sortField,
            String sortOrderSt, Integer take, Integer skip, @Suspended final AsyncResponse asyncResponse) {
        logger.debug("Device notification query requested for device {}", guid);

        final Date timestampSt = TimestampQueryParamParser.parse(startTs);
        final Date timestampEnd = TimestampQueryParamParser.parse(endTs);

        DeviceVO byGuidWithPermissionsCheck = deviceService.getDeviceWithNetworkAndDeviceClass(guid);
        if (byGuidWithPermissionsCheck == null) {
            ErrorResponse errorCode = new ErrorResponse(NOT_FOUND.getStatusCode(),
                    String.format(Messages.DEVICE_NOT_FOUND, guid));
            Response response = ResponseFactory.response(NOT_FOUND, errorCode);
            asyncResponse.resume(response);
        } else {
            Set<String> notificationNames = StringUtils.isNoneEmpty(notification)
                    ? Collections.singleton(notification)
                    : Collections.emptySet();
            notificationService.find(Collections.singleton(guid), notificationNames, timestampSt, timestampEnd)
                    .thenApply(notifications -> {
                        final Comparator<DeviceNotification> comparator = CommandResponseFilterAndSort
                                .buildDeviceNotificationComparator(sortField);
                        final Boolean reverse = sortOrderSt == null ? null : "desc".equalsIgnoreCase(sortOrderSt);

                        final List<DeviceNotification> sortedDeviceNotifications = CommandResponseFilterAndSort
                                .orderAndLimit(notifications, comparator, reverse, skip, take);
                        return ResponseFactory.response(OK, sortedDeviceNotifications,
                                JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT);
                    }).thenAccept(asyncResponse::resume);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void get(String guid, Long notificationId, @Suspended final AsyncResponse asyncResponse) {
        logger.debug("Device notification requested. Guid {}, notification id {}", guid, notificationId);

        DeviceVO device = deviceService.getDeviceWithNetworkAndDeviceClass(guid);

        if (device == null) {
            ErrorResponse errorCode = new ErrorResponse(NOT_FOUND.getStatusCode(),
                    String.format(Messages.DEVICE_NOT_FOUND, guid));
            Response response = ResponseFactory.response(NOT_FOUND, errorCode);
            asyncResponse.resume(response);
        } else {
            notificationService.findOne(notificationId, guid).thenApply(notification -> notification.map(n -> {
                logger.debug("Device notification proceed successfully");
                return ResponseFactory.response(Response.Status.OK, n, JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT);
            }).orElseGet(() -> {
                logger.warn(
                        "Device notification get failed. NOT FOUND: No notification with id = {} found for device with guid = {}",
                        notificationId, guid);
                ErrorResponse errorCode = new ErrorResponse(NOT_FOUND.getStatusCode(),
                        String.format(Messages.NOTIFICATION_NOT_FOUND, notificationId));
                return ResponseFactory.response(NOT_FOUND, errorCode);
            })).exceptionally(e -> {
                //TODO: change error message here
                logger.warn(
                        "Device notification get failed. NOT FOUND: No notification with id = {} found for device with guid = {}",
                        notificationId, guid);
                ErrorResponse errorCode = new ErrorResponse(NOT_FOUND.getStatusCode(),
                        String.format(Messages.NOTIFICATION_NOT_FOUND, notificationId));
                return ResponseFactory.response(NOT_FOUND, errorCode);
            }).thenAccept(asyncResponse::resume);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void poll(final String deviceGuid, final String namesString, final String timestamp, final long timeout,
            final AsyncResponse asyncResponse) throws Exception {
        poll(timeout, deviceGuid, namesString, timestamp, asyncResponse);
    }

    @Override
    public void pollMany(final long timeout, String deviceGuidsString, final String namesString,
            final String timestamp, final AsyncResponse asyncResponse) throws Exception {
        poll(timeout, deviceGuidsString, namesString, timestamp, asyncResponse);
    }

    private void poll(final long timeout, final String deviceGuidsString, final String namesString,
            final String timestamp, final AsyncResponse asyncResponse) throws InterruptedException {
        final HivePrincipal principal = (HivePrincipal) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();
        final Date ts = TimestampQueryParamParser
                .parse(timestamp == null ? timestampService.getDateAsString() : timestamp);

        final Response response = ResponseFactory.response(Response.Status.OK, Collections.emptyList(),
                JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT);

        asyncResponse.setTimeoutHandler(asyncRes -> asyncRes.resume(response));

        Set<String> availableDevices;
        if (deviceGuidsString == null) {
            availableDevices = deviceService.findByGuidWithPermissionsCheck(Collections.emptyList(), principal)
                    .stream().map(DeviceVO::getGuid).collect(Collectors.toSet());

        } else {
            availableDevices = Optional.ofNullable(StringUtils.split(deviceGuidsString, ',')).map(Arrays::asList)
                    .map(list -> deviceService.findByGuidWithPermissionsCheck(list, principal))
                    .map(list -> list.stream().map(DeviceVO::getGuid).collect(Collectors.toSet()))
                    .orElse(Collections.emptySet());
        }

        Set<String> notifications = Optional.ofNullable(StringUtils.split(namesString, ',')).map(Arrays::asList)
                .map(list -> list.stream().collect(Collectors.toSet())).orElse(Collections.emptySet());

        BiConsumer<DeviceNotification, String> callback = (notification, subscriptionId) -> {
            if (!asyncResponse.isDone()) {
                asyncResponse.resume(ResponseFactory.response(Response.Status.OK,
                        Collections.singleton(notification), JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT));
            }
        };

        if (!availableDevices.isEmpty()) {
            Pair<String, CompletableFuture<List<DeviceNotification>>> pair = notificationService
                    .subscribe(availableDevices, notifications, ts, callback);
            pair.getRight().thenAccept(collection -> {
                if (!collection.isEmpty() && !asyncResponse.isDone()) {
                    asyncResponse.resume(ResponseFactory.response(Response.Status.OK, collection,
                            JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT));
                }

                if (timeout == 0) {
                    asyncResponse.setTimeout(1, TimeUnit.MILLISECONDS); // setting timeout to 0 would cause
                    // the thread to suspend indefinitely, see AsyncResponse docs
                } else {
                    asyncResponse.setTimeout(timeout, TimeUnit.SECONDS);
                }
            });

            asyncResponse.register(new CompletionCallback() {
                @Override
                public void onComplete(Throwable throwable) {
                    notificationService.unsubscribe(pair.getLeft(), null);
                }
            });
        } else {
            if (!asyncResponse.isDone()) {
                asyncResponse.resume(response);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void insert(String guid, DeviceNotificationWrapper notificationSubmit,
            @Suspended final AsyncResponse asyncResponse) {
        logger.debug("DeviceNotification insert requested: {}", notificationSubmit);

        if (notificationSubmit.getNotification() == null) {
            logger.warn("DeviceNotification insert proceed with error. BAD REQUEST: notification is required.");
            ErrorResponse errorResponseEntity = new ErrorResponse(BAD_REQUEST.getStatusCode(),
                    Messages.INVALID_REQUEST_PARAMETERS);
            Response response = ResponseFactory.response(BAD_REQUEST, errorResponseEntity);
            asyncResponse.resume(response);
        } else {
            DeviceVO device = deviceService.getDeviceWithNetworkAndDeviceClass(guid);
            if (device == null) {
                logger.warn("DeviceNotification insert proceed with error. NOT FOUND: device {} not found.", guid);
                Response response = ResponseFactory.response(NOT_FOUND, new ErrorResponse(NOT_FOUND.getStatusCode(),
                        String.format(Messages.DEVICE_NOT_FOUND, guid)));
                asyncResponse.resume(response);
            } else {
                if (device.getNetwork() == null) {
                    logger.warn(
                            "DeviceNotification insert proceed with error. FORBIDDEN: Device {} is not connected to network.",
                            guid);
                    Response response = ResponseFactory.response(FORBIDDEN,
                            new ErrorResponse(FORBIDDEN.getStatusCode(),
                                    String.format(Messages.DEVICE_IS_NOT_CONNECTED_TO_NETWORK, guid)));
                    asyncResponse.resume(response);
                } else {
                    DeviceNotification toInsert = notificationService
                            .convertWrapperToNotification(notificationSubmit, device);
                    notificationService.insert(toInsert, device).thenAccept(notification -> {
                        logger.debug(
                                "Device notification insert proceed successfully. deviceId = {} notification = {}",
                                guid, notification.getNotification());

                        asyncResponse.resume(ResponseFactory.response(Response.Status.CREATED, notification,
                                JsonPolicyDef.Policy.NOTIFICATION_TO_CLIENT));
                    }).exceptionally(e -> {
                        // FIX ERROR
                        logger.warn("Device notification insert failed for device with guid = {}.", guid);
                        ErrorResponse errorCode = new ErrorResponse(NOT_FOUND.getStatusCode(),
                                String.format(Messages.NOTIFICATION_NOT_FOUND, -1L));
                        Response jaxResponse = ResponseFactory.response(NOT_FOUND, errorCode);
                        asyncResponse.resume(jaxResponse);
                        return null;
                    });
                }
            }
        }
    }
}