com.devicehive.service.DeviceCommandService.java Source code

Java tutorial

Introduction

Here is the source code for com.devicehive.service.DeviceCommandService.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.model.DeviceCommand;
import com.devicehive.model.eventbus.events.CommandEvent;
import com.devicehive.model.eventbus.events.CommandUpdateEvent;
import com.devicehive.model.rpc.*;
import com.devicehive.model.wrappers.DeviceCommandWrapper;
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.HiveValidator;
import com.devicehive.vo.DeviceVO;
import com.devicehive.vo.UserVO;
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 DeviceCommandService {

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

    private TimestampService timestampService;
    private HiveValidator hiveValidator;
    private RpcClient rpcClient;

    @Autowired
    public DeviceCommandService(TimestampService timestampService, HiveValidator hiveValidator,
            RpcClient rpcClient) {
        this.timestampService = timestampService;
        this.hiveValidator = hiveValidator;
        this.rpcClient = rpcClient;
    }

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

        CompletableFuture<Response> future = new CompletableFuture<>();
        rpcClient.call(Request.newBuilder().withBody(searchRequest).build(), new ResponseConsumer(future));
        return future
                .thenApply(r -> r.getBody().cast(CommandSearchResponse.class).getCommands().stream().findFirst());
    }

    public CompletableFuture<List<DeviceCommand>> find(Collection<String> guids, Collection<String> names,
            Date timestampSt, Date timestampEnd, String status) {
        List<CompletableFuture<Response>> futures = guids.stream().map(guid -> {
            CommandSearchRequest searchRequest = new CommandSearchRequest();
            searchRequest.setGuid(guid);
            if (names != null) {
                searchRequest.setNames(new HashSet<>(names));
            }
            searchRequest.setTimestampStart(timestampSt);
            searchRequest.setTimestampEnd(timestampEnd);
            searchRequest.setStatus(status);
            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<DeviceCommand>>
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
                .thenApply(v -> futures.stream().map(CompletableFuture::join) // List<CompletableFuture<Response>> => CompletableFuture<List<Response>>
                        .map(r -> ((CommandSearchResponse) r.getBody()).getCommands()) // CompletableFuture<List<Response>> => CompletableFuture<List<List<DeviceCommand>>>
                        .flatMap(Collection::stream) // CompletableFuture<List<List<DeviceCommand>>> => CompletableFuture<List<DeviceCommand>>
                        .collect(Collectors.toList()));
    }

    public CompletableFuture<DeviceCommand> insert(DeviceCommandWrapper commandWrapper, DeviceVO device,
            UserVO user) {
        DeviceCommand command = convertWrapperToCommand(commandWrapper, device, user);

        CompletableFuture<Response> future = new CompletableFuture<>();
        rpcClient.call(Request.newBuilder().withBody(new CommandInsertRequest(command))
                .withPartitionKey(device.getGuid()).build(), new ResponseConsumer(future));
        return future.thenApply(r -> ((CommandInsertResponse) r.getBody()).getDeviceCommand());
    }

    public Pair<String, CompletableFuture<List<DeviceCommand>>> sendSubscribeRequest(final Set<String> devices,
            final Set<String> names, final Date timestamp, final BiConsumer<DeviceCommand, String> callback)
            throws InterruptedException {

        final String subscriptionId = UUID.randomUUID().toString();
        Collection<CompletableFuture<Collection<DeviceCommand>>> futures = devices.stream()
                .map(device -> new CommandSubscribeRequest(subscriptionId, device, names, timestamp))
                .map(subscribeRequest -> {
                    CompletableFuture<Collection<DeviceCommand>> future = new CompletableFuture<>();
                    Consumer<Response> responseConsumer = response -> {
                        String resAction = response.getBody().getAction();
                        if (resAction.equals(Action.COMMAND_SUBSCRIBE_RESPONSE.name())) {
                            future.complete(response.getBody().cast(CommandSubscribeResponse.class).getCommands());
                        } else if (resAction.equals(Action.COMMAND_EVENT.name())) {
                            callback.accept(response.getBody().cast(CommandEvent.class).getCommand(),
                                    subscriptionId);
                        } else {
                            logger.warn("Unknown action received from backend {}", resAction);
                        }
                    };
                    Request request = Request.newBuilder().withBody(subscribeRequest)
                            .withPartitionKey(subscribeRequest.getDevice()).withSingleReply(false).build();
                    rpcClient.call(request, responseConsumer);
                    return future;
                }).collect(Collectors.toList());

        CompletableFuture<List<DeviceCommand>> 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 sendUnsubscribeRequest(String subId, Set<String> deviceGuids) {
        CommandUnsubscribeRequest unsubscribeRequest = new CommandUnsubscribeRequest(subId, deviceGuids);
        Request request = Request.newBuilder().withBody(unsubscribeRequest).build();
        rpcClient.push(request);
    }

    public CompletableFuture<Pair<String, DeviceCommand>> sendSubscribeToUpdateRequest(final long commandId,
            final String guid, BiConsumer<DeviceCommand, String> callback) {
        CompletableFuture<Pair<String, DeviceCommand>> future = new CompletableFuture<>();
        final String subscriptionId = UUID.randomUUID().toString();
        Consumer<Response> responseConsumer = response -> {
            String resAction = response.getBody().getAction();
            if (resAction.equals(Action.COMMAND_UPDATE_SUBSCRIBE_RESPONSE.name())) {
                future.complete(
                        Pair.of(response.getBody().cast(CommandUpdateSubscribeResponse.class).getSubscriptionId(),
                                response.getBody().cast(CommandUpdateSubscribeResponse.class).getDeviceCommand()));
            } else if (resAction.equals(Action.COMMAND_UPDATE_EVENT.name())) {
                callback.accept(response.getBody().cast(CommandUpdateEvent.class).getDeviceCommand(),
                        subscriptionId);
            } else {
                logger.warn("Unknown action received from backend {}", resAction);
            }
        };
        rpcClient.call(Request.newBuilder()
                .withBody(new CommandUpdateSubscribeRequest(commandId, guid, subscriptionId)).build(),
                responseConsumer);
        return future;
    }

    public CompletableFuture<Void> update(DeviceCommand cmd, DeviceCommandWrapper commandWrapper) {
        if (cmd == null) {
            throw new NoSuchElementException("Command not found");
        }
        cmd.setIsUpdated(true);

        if (commandWrapper.getCommand() != null) {
            cmd.setCommand(commandWrapper.getCommand().orElse(null));
        }
        if (commandWrapper.getTimestamp() != null && commandWrapper.getTimestamp().isPresent()) {
            cmd.setTimestamp(commandWrapper.getTimestamp().get());
        }
        if (commandWrapper.getParameters() != null) {
            cmd.setParameters(commandWrapper.getParameters().orElse(null));
        }
        if (commandWrapper.getLifetime() != null) {
            cmd.setLifetime(commandWrapper.getLifetime().orElse(null));
        }
        if (commandWrapper.getStatus() != null) {
            cmd.setStatus(commandWrapper.getStatus().orElse(null));
        }
        if (commandWrapper.getResult() != null) {
            cmd.setResult(commandWrapper.getResult().orElse(null));
        }

        hiveValidator.validate(cmd);

        CompletableFuture<Response> future = new CompletableFuture<>();
        rpcClient.call(Request.newBuilder().withBody(new CommandUpdateRequest(cmd)).build(),
                new ResponseConsumer(future));
        return future.thenApply(response -> null);
    }

    private DeviceCommand convertWrapperToCommand(DeviceCommandWrapper commandWrapper, DeviceVO device,
            UserVO user) {
        DeviceCommand command = new DeviceCommand();
        command.setId(Math.abs(new Random().nextInt()));
        command.setDeviceGuid(device.getGuid());
        command.setIsUpdated(false);

        if (commandWrapper.getTimestamp() != null && commandWrapper.getTimestamp().isPresent()) {
            command.setTimestamp(commandWrapper.getTimestamp().get());
        } else {
            command.setTimestamp(timestampService.getDate());
        }

        if (user != null) {
            command.setUserId(user.getId());
        }
        if (commandWrapper.getCommand() != null) {
            command.setCommand(commandWrapper.getCommand().orElseGet(null));
        }
        if (commandWrapper.getParameters() != null) {
            command.setParameters(commandWrapper.getParameters().orElse(null));
        }
        if (commandWrapper.getLifetime() != null) {
            command.setLifetime(commandWrapper.getLifetime().orElse(null));
        }
        if (commandWrapper.getStatus() != null) {
            command.setStatus(commandWrapper.getStatus().orElse(null));
        }
        if (commandWrapper.getResult() != null) {
            command.setResult(commandWrapper.getResult().orElse(null));
        }

        hiveValidator.validate(command);
        return command;
    }
}