/* * Copyright 2014-2016 CyberVision, Inc. * * 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. */ package org.kaaproject.kaa.server.operations.service; import org.kaaproject.kaa.common.dto.EndpointProfileDto; import org.kaaproject.kaa.common.dto.EndpointSpecificConfigurationDto; import org.kaaproject.kaa.common.dto.EndpointUserConfigurationDto; import org.kaaproject.kaa.common.dto.NotificationDto; import org.kaaproject.kaa.common.dto.TopicDto; import org.kaaproject.kaa.common.endpoint.security.MessageEncoderDecoder; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.kaaproject.kaa.common.hash.Sha1HashUtils; import org.kaaproject.kaa.server.common.Base64Util; import org.kaaproject.kaa.server.common.core.structure.Pair; import org.kaaproject.kaa.server.common.dao.EndpointService; import org.kaaproject.kaa.server.common.dao.EndpointSpecificConfigurationService; import org.kaaproject.kaa.server.common.dao.UserConfigurationService; import org.kaaproject.kaa.server.operations.pojo.GetDeltaRequest; import org.kaaproject.kaa.server.operations.pojo.GetDeltaResponse; import org.kaaproject.kaa.server.operations.pojo.GetNotificationRequest; import org.kaaproject.kaa.server.operations.pojo.GetNotificationResponse; import org.kaaproject.kaa.server.operations.pojo.RegisterProfileRequest; import org.kaaproject.kaa.server.operations.pojo.SyncContext; import org.kaaproject.kaa.server.operations.pojo.UpdateProfileRequest; import org.kaaproject.kaa.server.operations.pojo.exceptions.GetDeltaException; import org.kaaproject.kaa.server.operations.service.cache.AppSeqNumber; import org.kaaproject.kaa.server.operations.service.cache.CacheService; import org.kaaproject.kaa.server.operations.service.cache.ConfigurationCacheEntry; import org.kaaproject.kaa.server.operations.service.cache.TopicListCacheEntry; import org.kaaproject.kaa.server.operations.service.delta.DeltaService; import org.kaaproject.kaa.server.operations.service.delta.HistoryDelta; import org.kaaproject.kaa.server.operations.service.history.HistoryDeltaService; import org.kaaproject.kaa.server.operations.service.notification.NotificationDeltaService; import org.kaaproject.kaa.server.operations.service.profile.ProfileService; import org.kaaproject.kaa.server.operations.service.user.EndpointUserService; import org.kaaproject.kaa.server.sync.ClientSyncMetaData; import org.kaaproject.kaa.server.sync.ConfigurationClientSync; import org.kaaproject.kaa.server.sync.ConfigurationServerSync; import org.kaaproject.kaa.server.sync.EndpointAttachRequest; import org.kaaproject.kaa.server.sync.EndpointAttachResponse; import org.kaaproject.kaa.server.sync.EndpointDetachRequest; import org.kaaproject.kaa.server.sync.EndpointDetachResponse; import org.kaaproject.kaa.server.sync.EventClientSync; import org.kaaproject.kaa.server.sync.EventListenersRequest; import org.kaaproject.kaa.server.sync.EventListenersResponse; import org.kaaproject.kaa.server.sync.EventServerSync; import org.kaaproject.kaa.server.sync.Notification; import org.kaaproject.kaa.server.sync.NotificationClientSync; import org.kaaproject.kaa.server.sync.NotificationServerSync; import org.kaaproject.kaa.server.sync.NotificationType; import org.kaaproject.kaa.server.sync.ProfileClientSync; import org.kaaproject.kaa.server.sync.ProfileServerSync; import org.kaaproject.kaa.server.sync.ServerSync; import org.kaaproject.kaa.server.sync.SubscriptionType; import org.kaaproject.kaa.server.sync.SyncResponseStatus; import org.kaaproject.kaa.server.sync.SyncStatus; import org.kaaproject.kaa.server.sync.Topic; import org.kaaproject.kaa.server.sync.UserClientSync; import org.kaaproject.kaa.server.sync.UserServerSync; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Function; @Service public class DefaultOperationsService implements OperationsService { private static final Logger LOG = LoggerFactory.getLogger(DefaultOperationsService.class); @Autowired DeltaService deltaService; @Autowired ProfileService profileService; @Autowired CacheService cacheService; @Autowired HistoryDeltaService historyDeltaService; @Autowired NotificationDeltaService notificationDeltaService; @Autowired EndpointUserService endpointUserService; @Autowired UserConfigurationService userConfigurationService; @Autowired EndpointService endpointService; @Autowired EndpointSpecificConfigurationService endpointSpecificConfigurationService; private String operationServerHash; /** * Builds the notification sync response. * * @param notificationResponse the notification response * @return the notification sync response */ private static NotificationServerSync buildNotificationSyncResponse( GetNotificationResponse notificationResponse) { NotificationServerSync response = new NotificationServerSync(); response.setResponseStatus(SyncResponseStatus.NO_DELTA); if (notificationResponse.getNotifications() != null) { List<Notification> notifications = new ArrayList<>(); for (NotificationDto notificationDto : notificationResponse.getNotifications()) { notifications.add(convertNotification(notificationDto)); } response.setNotifications(notifications); } if (notificationResponse.getTopicList() != null) { List<Topic> topicList = new ArrayList<Topic>(); for (TopicDto topicDto : notificationResponse.getTopicList()) { Topic topic = new Topic(); topic.setId(topicDto.getId()); topic.setName(topicDto.getName()); switch (topicDto.getType()) { case MANDATORY: topic.setSubscriptionType(SubscriptionType.MANDATORY); break; case OPTIONAL: topic.setSubscriptionType(SubscriptionType.OPTIONAL); break; default: break; } topicList.add(topic); } response.setAvailableTopics(topicList); } if (notificationResponse.hasDelta()) { response.setResponseStatus(SyncResponseStatus.DELTA); } return response; } /** * Convert notification. * * @param notificationDto the notification dto * @return the notification */ private static Notification convertNotification(NotificationDto notificationDto) { Notification notification = new Notification(); notification.setBody(ByteBuffer.wrap(notificationDto.getBody())); notification.setTopicId(notificationDto.getTopicId()); switch (notificationDto.getType()) { case SYSTEM: notification.setType(NotificationType.SYSTEM); break; case USER: notification.setType(NotificationType.CUSTOM); break; default: break; } if (notificationDto.getSecNum() >= 0) { notification.setSeqNumber(notificationDto.getSecNum()); } else { // unicast notification notification.setUid(notificationDto.getId()); } return notification; } /** * Builds the conf sync response. * * @param deltaResponse the conf response * @return the conf sync response * @throws GetDeltaException the get delta exception */ private static ConfigurationServerSync buildConfSyncResponse(GetDeltaResponse deltaResponse) throws GetDeltaException { ConfigurationServerSync response = new ConfigurationServerSync(); if (deltaResponse.getDelta() != null) { try { response.setConfDeltaBody(ByteBuffer.wrap(deltaResponse.getDelta().getData())); } catch (IOException ex) { LOG.error("conf delta invalid: {}", ex); throw new GetDeltaException(ex); } } if (deltaResponse.getConfSchema() != null) { try { response.setConfSchemaBody( ByteBuffer.wrap(deltaResponse.getConfSchema().getBytes("UTF-8"))); } catch (UnsupportedEncodingException ex) { LOG.error("conf schema invalid: {}", ex); throw new GetDeltaException(ex); } } switch (deltaResponse.getResponseType()) { case CONF_RESYNC: response.setResponseStatus(SyncResponseStatus.RESYNC); break; case DELTA: response.setResponseStatus(SyncResponseStatus.DELTA); break; default: response.setResponseStatus(SyncResponseStatus.NO_DELTA); break; } return response; } /** * Checks if is first request. * * @param profile the profile * @return true, if is first request */ public static boolean isFirstRequest(EndpointProfileDto profile) { return profile.getGroupState() == null || profile.getGroupState().size() == 0; } /** * To byte array. * * @param buffer the buffer * @return the byte[] */ private static byte[] toByteArray(ByteBuffer buffer) { return Arrays.copyOf(buffer.array(), buffer.array().length); } @Override public SyncContext syncClientProfile(SyncContext context, ProfileClientSync profileSyncRequest) { EndpointProfileDto profile = context.getEndpointProfile(); ClientSyncMetaData metaData = context.getMetaData(); if (profileSyncRequest != null) { ProfileServerSync profileSyncResponse; if (profileSyncRequest.getEndpointPublicKey() != null) { LOG.debug("[{}][{}] registration of endpoint started.", context.getEndpointKey(), context.getRequestHash()); profile = registerEndpoint( context.getEndpointKey(), context.getRequestHash(), metaData, profileSyncRequest); } else { LOG.debug("[{}][{}] update of endpoint profile started.", context.getEndpointKey(), context.getRequestHash()); profile = updateEndpoint(context.getEndpointKey(), context.getRequestHash(), metaData, profileSyncRequest); } profileSyncResponse = new ProfileServerSync(SyncResponseStatus.DELTA); metaData.setProfileHash(ByteBuffer.wrap(profile.getProfileHash())); context.setProfileSyncResponse(profileSyncResponse); } if (profile == null) { LOG.debug("[{}][{}] fetching profile.", context.getEndpointKey(), context.getRequestHash()); EndpointObjectHash endpointHash = EndpointObjectHash.fromBytes( metaData.getEndpointPublicKeyHash().array()); profile = profileService.getProfile(endpointHash); LOG.trace("[{}][{}] fetched profile {}.", context.getEndpointKey(), context.getRequestHash(), profile); } if (!Arrays.equals(profile.getProfileHash(), toByteArray(metaData.getProfileHash()))) { LOG.debug("[{}] Profile hash mismatch. Profile resync needed", context.getEndpointKey()); if (LOG.isTraceEnabled()) { LOG.trace("[{}] persisted profile hash is {}", context.getEndpointKey(), MessageEncoderDecoder.bytesToHex(profile.getProfileHash())); LOG.trace("[{}] client profile hash is {}", context.getEndpointKey(), MessageEncoderDecoder.bytesToHex(toByteArray(metaData.getProfileHash()))); } context.setStatus(SyncStatus.PROFILE_RESYNC); } profile = syncProfileState( metaData.getApplicationToken(), context.getEndpointKey(), profile, false); context.setNotificationVersion(profile); return context; } @Override public EndpointProfileDto syncServerProfile(String appToken, String endpointKey, EndpointObjectHash key) { EndpointProfileDto profile = refreshServerEndpointProfile(key); profile.setGroupState(new ArrayList<>()); profile = syncProfileState(appToken, endpointKey, profile, false); return profile; } @Override public SyncContext processEndpointAttachDetachRequests(SyncContext context, UserClientSync request) { if (request != null) { LOG.trace("[{}][{}] procesing user sync request {}.", context.getEndpointKey(), context.getRequestHash(), request); UserServerSync userSyncResponse = processUserSyncRequest( context.getEndpointKey(), context.getRequestHash(), request, context.getEndpointProfile()); context.setUserSyncResponse(userSyncResponse); } return context; } @Override public SyncContext processEventListenerRequests(SyncContext context, EventClientSync request) { if (request != null) { ClientSyncMetaData metaData = context.getMetaData(); LOG.trace("[{}][{}] procesing event sync request {}.", context.getEndpointKey(), context.getRequestHash(), request); EventServerSync eventSyncResponse = processEventSyncResponse( context.getEndpointKey(), context.getRequestHash(), metaData.getApplicationToken(), request, context.getEndpointProfile()); context.setEventSyncResponse(eventSyncResponse); } return context; } private EndpointProfileDto syncProfileState(String appToken, String endpointId, EndpointProfileDto endpointProfile, boolean updateConfiguration) { LOG.debug("[{}][{}] going to sync endpoint group states", appToken, endpointId); Function<EndpointProfileDto, Pair<EndpointProfileDto, HistoryDelta>> updateFunction = profile -> { AppSeqNumber appSeqNumber = cacheService.getAppSeqNumber(appToken); int curAppSeqNumber = appSeqNumber.getSeqNumber(); HistoryDelta historyDelta = fetchHistory(endpointId, appToken, profile, curAppSeqNumber); profile.setGroupState(historyDelta.getEndpointGroupStates()); profile.setSequenceNumber(curAppSeqNumber); if (historyDelta.isConfigurationChanged() || updateConfiguration) { LOG.debug("[{}][{}] configuration change detected", appToken, endpointId); try { syncEndpointConfiguration(appToken, endpointId, profile); } catch (GetDeltaException ex) { // TODO: Figure out how to act in case of failover here. LOG.error("[{}][{}] Failed to sync endpoint configuration {}", appToken, endpointId, ex); } } if (historyDelta.isTopicListChanged()) { LOG.debug("[{}][{}] topic list change detected", appToken, endpointId); syncTopicList(appToken, endpointId, profile); } return new Pair<>(profile, historyDelta); }; Pair<EndpointProfileDto, HistoryDelta> result = updateFunction.apply(endpointProfile); endpointProfile = result.getV1(); HistoryDelta historyDelta = result.getV2(); if (historyDelta.isSmthChanged() || updateConfiguration) { LOG.debug("[{}][{}] going to save new profile", appToken, endpointId); endpointProfile = profileService.updateProfile(endpointProfile, (storedProfile, newProfile) -> { if (updateConfiguration) { storedProfile.setUserConfigurationHash(newProfile.getUserConfigurationHash()); storedProfile.setEpsConfigurationHash(newProfile.getEpsConfigurationHash()); } storedProfile.setGroupState(new ArrayList<>()); return updateFunction.apply(storedProfile).getV1(); }); } return endpointProfile; } private void syncEndpointConfiguration(String appToken, String endpointId, EndpointProfileDto profile) throws GetDeltaException { ConfigurationCacheEntry configurationCache = deltaService.getConfiguration( appToken, endpointId, profile); byte[] configurationHash = configurationCache.getHash().getData(); if (LOG.isTraceEnabled()) { LOG.trace("[{}][{}] Result configuration hash is {}", appToken, endpointId, Arrays.toString(configurationHash)); } profile.setConfigurationHash(configurationHash); EndpointObjectHash userConfigHash = configurationCache.getUserConfigurationHash(); profile.setUserConfigurationHash(userConfigHash == null ? null : userConfigHash.getData()); EndpointObjectHash epsConfigHash = configurationCache.getEpsConfigurationHash(); profile.setEpsConfigurationHash(epsConfigHash == null ? null : epsConfigHash.getData()); } private void syncTopicList(String appToken, String endpointId, EndpointProfileDto profile) { TopicListCacheEntry topicListCache = notificationDeltaService.getTopicListHash( appToken, endpointId, profile); if (LOG.isTraceEnabled()) { LOG.trace("[{}][{}] Result topic hash is {}", appToken, endpointId, topicListCache); } profile.setSimpleTopicHash(topicListCache.getSimpleHash()); profile.setTopicHash(topicListCache.getHash().getData()); } @Override public SyncContext syncConfiguration(SyncContext context, ConfigurationClientSync request) throws GetDeltaException { if (request != null) { GetDeltaResponse confResponse = calculateConfigurationDelta( context.getMetaData().getApplicationToken(), request, context); ConfigurationServerSync confSyncResponse = buildConfSyncResponse(confResponse); context.setConfigurationSyncResponse(confSyncResponse); } return context; } @Override public SyncContext syncConfigurationHashes(SyncContext context, byte[] ucfHash, byte[] epsConfigHash) { EndpointProfileDto profile = context.getEndpointProfile(); profile.setUserConfigurationHash(ucfHash); profile.setEpsConfigurationHash(epsConfigHash); syncProfileState(context.getAppToken(), context.getEndpointKey(), profile, true); return context; } @Override public SyncContext syncUseConfigurationRawSchema(SyncContext context, boolean useConfigurationRawSchema) { EndpointProfileDto profile = context.getEndpointProfile(); if (profile.isUseConfigurationRawSchema() != useConfigurationRawSchema) { ClientSyncMetaData metaData = context.getMetaData(); EndpointObjectHash endpointKeyHash = EndpointObjectHash.fromBytes( toByteArray(metaData.getEndpointPublicKeyHash())); profile = profileService.updateProfile(metaData, endpointKeyHash, useConfigurationRawSchema); profile = syncProfileState( metaData.getApplicationToken(), context.getEndpointKey(), profile, false); context.setNotificationVersion(profile); } return context; } @Override public SyncContext syncNotification(SyncContext context, NotificationClientSync request) { if (request != null) { GetNotificationResponse notificationResponse = calculateNotificationDelta( context.getMetaData().getApplicationToken(), request, context); context.setSubscriptionStates(notificationResponse.getSubscriptionStates()); NotificationServerSync nfSyncResponse = buildNotificationSyncResponse(notificationResponse); context.setNotificationSyncResponse(nfSyncResponse); if (notificationResponse.isSubscriptionListChanged()) { EndpointProfileDto profileDto = context.getEndpointProfile(); Function<EndpointProfileDto, EndpointProfileDto> updateFunction = profile -> { profile.setSubscriptions(new ArrayList<>(notificationResponse.getSubscriptionSet())); return profile; }; context.setNotificationVersion(profileService.updateProfile( updateFunction.apply(profileDto), (storedProfile, newProfile) -> { return updateFunction.apply(storedProfile); })); } } return context; } /** * Process sync. */ @Override public SyncContext syncProfileServerHash(SyncContext context) { EndpointProfileDto profile = context.getEndpointProfile(); if (!operationServerHash.equals(profile.getServerHash())) { LOG.debug("[{}] Operations server hash changed from {} to {}", context.getEndpointKey(), profile.getServerHash(), operationServerHash); profile.setServerHash(operationServerHash); context.setNotificationVersion(profileService.updateProfile(profile, (storedProfile, newProfile) -> storedProfile)); } return context; } @Override public byte[] fetchUcfHash(String appToken, EndpointProfileDto profile) { if (profile.getEndpointUserId() == null || profile.getEndpointUserId().isEmpty()) { return null; } EndpointUserConfigurationDto ucfDto = userConfigurationService.findUserConfigurationByUserIdAndAppTokenAndSchemaVersion( profile.getEndpointUserId(), appToken, profile.getConfigurationVersion()); if (ucfDto != null) { return EndpointObjectHash.fromString(ucfDto.getBody()).getData(); } return null; } @Override public byte[] fetchEndpointSpecificConfigurationHash(EndpointProfileDto profile) { Optional<EndpointSpecificConfigurationDto> configuration = endpointSpecificConfigurationService.findActiveConfigurationByEndpointProfile(profile); return configuration.filter(conf -> conf.getConfiguration() != null) .map(dto -> EndpointObjectHash.fromString(dto.getConfiguration()).getData()).orElse(null); } private EndpointProfileDto registerEndpoint(String endpointId, int requestHash, ClientSyncMetaData metaData, ProfileClientSync request) { LOG.debug("[{}][{}] register endpoint. request: {}", endpointId, requestHash, request); byte[] endpointKey = toByteArray(request.getEndpointPublicKey()); byte[] profileBody = toByteArray(request.getProfileBody()); RegisterProfileRequest registerProfileRequest = new RegisterProfileRequest( metaData.getApplicationToken(), endpointKey, metaData.getSdkToken(), profileBody, request.getEndpointAccessToken()); EndpointProfileDto endpointProfile = profileService.registerProfile(registerProfileRequest); LOG.debug("profile registered. id: {}, endpointKeyHash: {}", endpointProfile.getId(), endpointProfile.getEndpointKeyHash()); return endpointProfile; } private EndpointProfileDto updateEndpoint(String endpointId, int requestHash, ClientSyncMetaData metaData, ProfileClientSync request) { LOG.debug("[{}][{}] update endpoint. request: {}", endpointId, requestHash, request); EndpointObjectHash endpointKeyHash = EndpointObjectHash.fromBytes( toByteArray(metaData.getEndpointPublicKeyHash())); UpdateProfileRequest updateRequest = new UpdateProfileRequest( metaData.getApplicationToken(), endpointKeyHash, request.getEndpointAccessToken(), request.getProfileBody().array(), metaData.getSdkToken()); EndpointProfileDto endpointProfile = profileService.updateProfile(updateRequest); LOG.debug("profile updated. id: {}, endpointKeyHash: {}", endpointProfile.getId(), endpointProfile.getEndpointKeyHash()); return endpointProfile; } private EventServerSync processEventSyncResponse(String endpointId, int requestHash, String appToken, EventClientSync request, EndpointProfileDto profile) { EventServerSync response = new EventServerSync(); List<EventListenersRequest> requests = request.getEventListenersRequests(); if (requests != null && !requests.isEmpty()) { LOG.debug("[{}] processing {} endpoint listener requests", endpointId, requests.size()); List<EventListenersResponse> responses = new ArrayList<>(requests.size()); for (EventListenersRequest elRequest : requests) { LOG.debug("[{}] processing event listener request {}", endpointId, request); EventListenersResponse elResponse = endpointUserService.findListeners( profile, appToken, elRequest); LOG.debug("[{}] event listener response {}", endpointId, response); responses.add(elResponse); } response.setEventListenersResponses(responses); } else { List<EventListenersResponse> emptyList = Collections.emptyList(); response.setEventListenersResponses(emptyList); } return response; } private UserServerSync processUserSyncRequest(String endpointId, int requestHash, UserClientSync request, EndpointProfileDto profile) { UserServerSync response = new UserServerSync(); if (request.getEndpointAttachRequests() != null) { response.setEndpointAttachResponses(processEndpointAttachRequests( endpointId, requestHash, request, profile)); } if (request.getEndpointDetachRequests() != null) { response.setEndpointDetachResponses(processEndpointDetachRequests( endpointId, requestHash, request, profile)); } return response; } private List<EndpointAttachResponse> processEndpointAttachRequests(String endpointId, int requestHash, UserClientSync syncRequest, EndpointProfileDto profile) { List<EndpointAttachRequest> requests = syncRequest.getEndpointAttachRequests(); if (requests != null && !requests.isEmpty()) { LOG.debug("[{}][{}] processing {} endpoint attach requests", endpointId, requestHash, requests.size()); List<EndpointAttachResponse> responses = new ArrayList<>(requests.size()); for (EndpointAttachRequest request : syncRequest.getEndpointAttachRequests()) { LOG.debug("[{}][{}] processing endpoint attach request {}", endpointId, requestHash, request); EndpointAttachResponse response = endpointUserService.attachEndpoint(profile, request); LOG.debug("[{}][{}] endpoint attach response {}", endpointId, requestHash, response); responses.add(response); } return responses; } else { return Collections.emptyList(); } } private List<EndpointDetachResponse> processEndpointDetachRequests(String endpointId, int requestHash, UserClientSync syncRequest, EndpointProfileDto profile) { List<EndpointDetachRequest> requests = syncRequest.getEndpointDetachRequests(); if (requests != null && !requests.isEmpty()) { LOG.debug("[{}] processing {} endpoint detach requests", endpointId, requests.size()); List<EndpointDetachResponse> responses = new ArrayList<>(requests.size()); for (EndpointDetachRequest request : requests) { LOG.debug("[{}] processing endpoint detach request {}", endpointId, request); EndpointDetachResponse response = endpointUserService.detachEndpoint(profile, request); LOG.debug("[{}] endpoint detach response {}", endpointId, response); responses.add(response); } return responses; } else { return Collections.emptyList(); } } /** * Calculate notification delta. * * @param appToken application token * @param context context * @param syncRequest the sync request * @return the gets the notification response */ private GetNotificationResponse calculateNotificationDelta(String appToken, NotificationClientSync syncRequest, SyncContext context) { GetNotificationRequest request = new GetNotificationRequest( syncRequest.getTopicListHash(), context.getEndpointProfile(), syncRequest.getSubscriptionCommands(), syncRequest.getAcceptedUnicastNotifications(), syncRequest.getTopicStates()); return notificationDeltaService.getNotificationDelta(request); } /** * Calculate configuration delta. * * @param appToken the application token * @param request the request * @param context the context * @return the gets the delta response * @throws GetDeltaException the get delta exception */ private GetDeltaResponse calculateConfigurationDelta(String appToken, ConfigurationClientSync request, SyncContext context) throws GetDeltaException { GetDeltaRequest deltaRequest; if (request.getConfigurationHash() != null) { deltaRequest = new GetDeltaRequest( appToken, EndpointObjectHash.fromBytes(request.getConfigurationHash().array()), request.isResyncOnly()); } else { deltaRequest = new GetDeltaRequest(appToken); } deltaRequest.setEndpointProfile(context.getEndpointProfile()); return deltaService.getDelta(deltaRequest); } /** * Fetch history. * * @param endpointKey the endpoint id * @param applicationToken the application token * @param profile the profile * @param endSeqNumber the end seq number * @return the history delta */ private HistoryDelta fetchHistory(String endpointKey, String applicationToken, EndpointProfileDto profile, int endSeqNumber) { if (isFirstRequest(profile)) { LOG.debug("[{}] Profile has no endpoint groups yet. Calculating full list", endpointKey); return historyDeltaService.getDelta(profile, applicationToken, endSeqNumber); } else { LOG.debug("[{}] Profile has endpoint groups. Calculating changes from {} to {}", endpointKey, profile.getSequenceNumber(), endSeqNumber); return historyDeltaService.getDelta( profile, applicationToken, profile.getSequenceNumber(), endSeqNumber); } } /* * (non-Javadoc) * * @see org.kaaproject.kaa.server.operations.service.OperationsService# * updateSyncResponse(org.kaaproject.kaa.common.endpoint.gen.SyncResponse, * java.util.List, java.lang.String) */ @Override public ServerSync updateSyncResponse(ServerSync response, List<NotificationDto> notificationDtos, String unicastNotificationId) { LOG.debug("Updating sync response {}", response); boolean modified = false; NotificationServerSync notificationResponse = response.getNotificationSync(); if (notificationResponse == null) { notificationResponse = new NotificationServerSync(); response.setNotificationSync(notificationResponse); } List<Notification> notifications = notificationResponse.getNotifications(); if (notifications == null) { notifications = new ArrayList<Notification>(); } for (NotificationDto notificationDto : notificationDtos) { Notification newNotification = convertNotification(notificationDto); boolean found = false; for (Notification oldNotification : notifications) { if (oldNotification.getSeqNumber() == newNotification.getSeqNumber()) { found = true; break; } } if (!found) { modified = true; notifications.add(newNotification); } else { LOG.debug("Notification with seq number {} is already present in response", newNotification.getSeqNumber()); } } if (unicastNotificationId != null) { boolean found = false; for (Notification oldNotification : notifications) { if (oldNotification.getUid() != null && oldNotification.getUid().equals(unicastNotificationId)) { found = true; break; } } if (!found) { modified = true; NotificationDto unicast = notificationDeltaService.findUnicastNotificationById( unicastNotificationId); if (unicast != null) { notifications.add(convertNotification(unicast)); } else { LOG.warn("Notification with id {} was not found! " + "Possible duplication of events from client.", unicastNotificationId); } } else { LOG.debug("Notification with uid [{}] is already present in response", unicastNotificationId); } } if (modified) { notificationResponse.setNotifications(notifications); notificationResponse.setResponseStatus(SyncResponseStatus.DELTA); LOG.debug("Updated sync response {}", response); return response; } LOG.debug("Sync response was not updated!"); return null; } @Override public void setPublicKey(PublicKey publicKey) { operationServerHash = Base64Util.encode(Sha1HashUtils.hashToBytes(publicKey.getEncoded())); } @Override public EndpointProfileDto attachEndpointToUser(EndpointProfileDto profile, String appToken, String userExternalId) { return endpointUserService.attachEndpointToUser(profile, appToken, userExternalId); } @Override public EndpointProfileDto refreshServerEndpointProfile(EndpointObjectHash key) { return profileService.getProfile(key); } }