package com.devicehive.service; /* * #%L * DeviceHive Java Server Common business 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.configuration.Messages; import com.devicehive.dao.DeviceClassDao; import com.devicehive.exceptions.HiveException; import com.devicehive.model.rpc.ListDeviceClassRequest; import com.devicehive.model.rpc.ListDeviceClassResponse; import com.devicehive.model.updates.DeviceClassUpdate; import com.devicehive.model.updates.EquipmentUpdate; import com.devicehive.service.helpers.ResponseConsumer; import com.devicehive.shim.api.Request; import com.devicehive.shim.api.client.RpcClient; import com.devicehive.util.HiveValidator; import com.devicehive.vo.DeviceClassEquipmentVO; import com.devicehive.vo.DeviceClassWithEquipmentVO; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.validation.constraints.NotNull; import javax.ws.rs.core.Response; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import static javax.ws.rs.core.Response.Status.FORBIDDEN; import static javax.ws.rs.core.Response.Status.NOT_FOUND; @Component public class DeviceClassService { @Autowired private HiveValidator hiveValidator; @Autowired private DeviceClassDao deviceClassDao; @Autowired private RpcClient rpcClient; @Transactional public void delete(@NotNull long id) { deviceClassDao.remove(id); } @Transactional(propagation = Propagation.SUPPORTS) public DeviceClassWithEquipmentVO getWithEquipment(@NotNull Long id) { return deviceClassDao.find(id); } @Transactional public DeviceClassWithEquipmentVO createOrUpdateDeviceClass(Optional<DeviceClassUpdate> deviceClass, Set<DeviceClassEquipmentVO> customEquipmentSet) { DeviceClassWithEquipmentVO stored; //use existing if (deviceClass == null || !deviceClass.isPresent()) { return null; } //check is already done DeviceClassUpdate deviceClassUpdate = deviceClass.orElse(null); DeviceClassWithEquipmentVO deviceClassFromMessage = deviceClassUpdate.convertTo(); if (deviceClassFromMessage.getId() != null) { stored = deviceClassDao.find(deviceClassFromMessage.getId()); } else { stored = deviceClassDao.findByName(deviceClassFromMessage.getName()); } if (stored != null) { if (!stored.getIsPermanent()) { deviceClassUpdate.setEquipment(Optional.ofNullable(customEquipmentSet)); update(stored.getId(), deviceClassUpdate); } return stored; } else { return addDeviceClass(deviceClassFromMessage); } } @Transactional public DeviceClassWithEquipmentVO addDeviceClass(DeviceClassWithEquipmentVO deviceClass) { if (deviceClassDao.findByName(deviceClass.getName()) != null) { throw new HiveException(Messages.DEVICE_CLASS_WITH_SUCH_NAME_AND_VERSION_EXISTS, FORBIDDEN.getStatusCode()); } if (deviceClass.getIsPermanent() == null) { deviceClass.setIsPermanent(false); } hiveValidator.validate(deviceClass); deviceClass = deviceClassDao.persist(deviceClass); // TODO [rafa] looks strange, very strange // if (deviceClass.getEquipment() != null) { // Set<Equipment> resultEquipment = createEquipment(deviceClass, deviceClass.getEquipment()); // deviceClass.setEquipment(resultEquipment); // } return deviceClass; } @Transactional public DeviceClassWithEquipmentVO update(@NotNull Long id, DeviceClassUpdate update) { DeviceClassWithEquipmentVO stored = deviceClassDao.find(id); if (stored == null) { throw new HiveException(String.format(Messages.DEVICE_CLASS_NOT_FOUND, id), Response.Status.NOT_FOUND.getStatusCode()); } if (update == null) { return null; } if (update.getData() != null) { stored.setData(update.getData().orElse(null)); } if (update.getEquipment() != null) { if (update.getEquipment().isPresent()) { Map<String, DeviceClassEquipmentVO> existing = new HashMap<>(); for (DeviceClassEquipmentVO deviceClassEquipmentVO : stored.getEquipment()) { existing.put(deviceClassEquipmentVO.getCode(), deviceClassEquipmentVO); } for (DeviceClassEquipmentVO deviceClassEquipmentVO : update.getEquipment().get()) { if (existing.containsKey(deviceClassEquipmentVO.getCode())) { Long existingEquipmentId = existing.get(deviceClassEquipmentVO.getCode()).getId(); deviceClassEquipmentVO.setId(existingEquipmentId); } } stored.setEquipment(update.getEquipment().orElse(null)); } } if (update.getId() != null) { stored.setId(update.getId()); } if (update.getName() != null) { stored.setName(update.getName().orElse(null)); } if (update.getPermanent() != null) { stored.setIsPermanent(update.getPermanent().orElse(null)); } hiveValidator.validate(stored); return deviceClassDao.merge(stored); } /** * updates Equipment attributes * * @param equipmentUpdate Equipment instance, containing fields to update (Id field will be ignored) * @param equipmentId id of equipment to update * @param deviceClassId class of equipment to update * @return true, if update successful, false otherwise */ @Transactional public boolean update(EquipmentUpdate equipmentUpdate, @NotNull long equipmentId, @NotNull long deviceClassId) { if (equipmentUpdate == null) { return true; } DeviceClassWithEquipmentVO stored = deviceClassDao.find(deviceClassId); DeviceClassEquipmentVO found = null; for (DeviceClassEquipmentVO deviceClassEquipmentVO : stored.getEquipment()) { if (deviceClassEquipmentVO.getId().equals(equipmentId)) { found = deviceClassEquipmentVO; } } if (found == null) { return false; // equipment with id = equipmentId does not exists } if (equipmentUpdate.getCode() != null) { found.setCode(equipmentUpdate.getCode().orElse(null)); } if (equipmentUpdate.getName() != null) { found.setName(equipmentUpdate.getName().orElse(null)); } if (equipmentUpdate.getType() != null) { found.setType(equipmentUpdate.getType().orElse(null)); } if (equipmentUpdate.getData() != null) { found.setData(equipmentUpdate.getData().orElse(null)); } deviceClassDao.merge(stored); return true; } @Transactional public Set<DeviceClassEquipmentVO> createEquipment(@NotNull Long classId, @NotNull Set<DeviceClassEquipmentVO> equipments) { DeviceClassWithEquipmentVO deviceClass = deviceClassDao.find(classId); Set<String> existingCodesSet = deviceClass.getEquipment().stream().map(DeviceClassEquipmentVO::getCode).collect(Collectors.toSet()); Set<String> newCodeSet = equipments.stream().map(DeviceClassEquipmentVO::getCode).collect(Collectors.toSet()); newCodeSet.retainAll(existingCodesSet); if (!newCodeSet.isEmpty()) { String codeSet = StringUtils.join(newCodeSet, ","); throw new HiveException(String.format(Messages.DUPLICATE_EQUIPMENT_ENTRY, codeSet, classId), FORBIDDEN.getStatusCode()); } deviceClass.getEquipment().addAll(equipments); deviceClass = deviceClassDao.merge(deviceClass); //TODO [rafa] duuumb, and lazy, in case of several equipments linked to the class two for loops is fine. for (DeviceClassEquipmentVO equipment : equipments) { for (DeviceClassEquipmentVO s : deviceClass.getEquipment()) { if (equipment.getCode().equals(s.getCode())) { equipment.setId(s.getId()); } } } return equipments; } @Transactional public DeviceClassEquipmentVO createEquipment(Long classId, DeviceClassEquipmentVO equipment) { DeviceClassWithEquipmentVO deviceClass = deviceClassDao.find(classId); if (deviceClass == null) { throw new HiveException(String.format(Messages.DEVICE_CLASS_NOT_FOUND, classId), NOT_FOUND.getStatusCode()); } if (deviceClass.getIsPermanent()) { throw new HiveException(Messages.UPDATE_PERMANENT_EQUIPMENT, NOT_FOUND.getStatusCode()); } Set<DeviceClassEquipmentVO> equipments = deviceClass.getEquipment(); String newCode = equipment.getCode(); if (equipments != null) { for (DeviceClassEquipmentVO e : equipments) { if (newCode.equals(e.getCode())) { String errorMessage = String.format(Messages.DUPLICATE_EQUIPMENT_ENTRY, e.getCode(), classId); throw new HiveException(errorMessage, FORBIDDEN.getStatusCode()); } } } deviceClass.getEquipment().add(equipment); deviceClass = deviceClassDao.merge(deviceClass); //TODO [rafa] find device equipment class back from the set in device class. for (DeviceClassEquipmentVO s : deviceClass.getEquipment()) { if (equipment.getCode().equals(s.getCode())) { equipment.setId(s.getId()); } } return equipment; } //@Transactional(propagation = Propagation.NOT_SUPPORTED) public CompletableFuture<List<DeviceClassWithEquipmentVO>> list(String name, String namePattern, String sortField, Boolean sortOrderAsc, Integer take, Integer skip) { ListDeviceClassRequest request = new ListDeviceClassRequest(); request.setName(name); request.setNamePattern(namePattern); request.setSortField(sortField); request.setSortOrderAsc(sortOrderAsc); request.setTake(take); request.setSkip(skip); CompletableFuture<com.devicehive.shim.api.Response> future = new CompletableFuture<>(); rpcClient.call(Request .newBuilder() .withBody(request) .build(), new ResponseConsumer(future)); return future.thenApply(r -> ((ListDeviceClassResponse) r.getBody()).getDeviceClasses()); } /** * Delete Equipment (not DeviceEquipment, but whole equipment with appropriate device Equipments) * * @param equipmentId equipment id to delete * @param deviceClassId id of deviceClass which equipment belongs used to double check * @return true if deleted successfully */ @Transactional public boolean delete(@NotNull long equipmentId, @NotNull long deviceClassId) { DeviceClassWithEquipmentVO stored = deviceClassDao.find(deviceClassId); DeviceClassEquipmentVO found = null; Iterator<DeviceClassEquipmentVO> iterator = stored.getEquipment().iterator(); while (iterator.hasNext()) { DeviceClassEquipmentVO deviceClassEquipmentVO = iterator.next(); if (deviceClassEquipmentVO.getId().equals(equipmentId)) { iterator.remove(); found = deviceClassEquipmentVO; } } if (found != null) { deviceClassDao.merge(stored); } return found != null; } /** * Retrieves Equipment from database * * @param deviceClassId parent device class id for this equipment * @param equipmentId id of equipment to get */ @Transactional(readOnly = true) public DeviceClassEquipmentVO getByDeviceClass(@NotNull long deviceClassId, @NotNull long equipmentId) { return deviceClassDao.findDeviceClassEquipment(deviceClassId, equipmentId); } /** * Retrieves Equipment from database * * @param deviceClassId parent device class id for this equipment */ @Transactional(readOnly = true) public Set<DeviceClassEquipmentVO> getByDeviceClass(@NotNull long deviceClassId) { return deviceClassDao.find(deviceClassId).getEquipment(); } }