use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.
the class NotificationControllerV2 method pollNotification.
@GetMapping
public DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> pollNotification(@RequestParam(value = "appId") String appId, @RequestParam(value = "cluster") String cluster, @RequestParam(value = "notifications") String notificationsAsString, @RequestParam(value = "dataCenter", required = false) String dataCenter, @RequestParam(value = "ip", required = false) String clientIp) {
List<ApolloConfigNotification> notifications = null;
try {
notifications = gson.fromJson(notificationsAsString, notificationsTypeReference);
} catch (Throwable ex) {
Tracer.logError(ex);
}
if (CollectionUtils.isEmpty(notifications)) {
throw new BadRequestException("Invalid format of notifications: " + notificationsAsString);
}
Map<String, ApolloConfigNotification> filteredNotifications = filterNotifications(appId, notifications);
if (CollectionUtils.isEmpty(filteredNotifications)) {
throw new BadRequestException("Invalid format of notifications: " + notificationsAsString);
}
DeferredResultWrapper deferredResultWrapper = new DeferredResultWrapper(bizConfig.longPollingTimeoutInMilli());
Set<String> namespaces = Sets.newHashSetWithExpectedSize(filteredNotifications.size());
Map<String, Long> clientSideNotifications = Maps.newHashMapWithExpectedSize(filteredNotifications.size());
for (Map.Entry<String, ApolloConfigNotification> notificationEntry : filteredNotifications.entrySet()) {
String normalizedNamespace = notificationEntry.getKey();
ApolloConfigNotification notification = notificationEntry.getValue();
namespaces.add(normalizedNamespace);
clientSideNotifications.put(normalizedNamespace, notification.getNotificationId());
if (!Objects.equals(notification.getNamespaceName(), normalizedNamespace)) {
deferredResultWrapper.recordNamespaceNameNormalizedResult(notification.getNamespaceName(), normalizedNamespace);
}
}
Multimap<String, String> watchedKeysMap = watchKeysUtil.assembleAllWatchKeys(appId, cluster, namespaces, dataCenter);
Set<String> watchedKeys = Sets.newHashSet(watchedKeysMap.values());
/**
* 1、set deferredResult before the check, for avoid more waiting
* If the check before setting deferredResult,it may receive a notification the next time
* when method handleMessage is executed between check and set deferredResult.
*/
deferredResultWrapper.onTimeout(() -> logWatchedKeys(watchedKeys, "Apollo.LongPoll.TimeOutKeys"));
deferredResultWrapper.onCompletion(() -> {
// unregister all keys
for (String key : watchedKeys) {
deferredResults.remove(key, deferredResultWrapper);
}
logWatchedKeys(watchedKeys, "Apollo.LongPoll.CompletedKeys");
});
// register all keys
for (String key : watchedKeys) {
this.deferredResults.put(key, deferredResultWrapper);
}
logWatchedKeys(watchedKeys, "Apollo.LongPoll.RegisteredKeys");
logger.debug("Listening {} from appId: {}, cluster: {}, namespace: {}, datacenter: {}", watchedKeys, appId, cluster, namespaces, dataCenter);
/**
* 2、check new release
*/
List<ReleaseMessage> latestReleaseMessages = releaseMessageService.findLatestReleaseMessagesGroupByMessages(watchedKeys);
/**
* Manually close the entity manager.
* Since for async request, Spring won't do so until the request is finished,
* which is unacceptable since we are doing long polling - means the db connection would be hold
* for a very long time
*/
entityManagerUtil.closeEntityManager();
List<ApolloConfigNotification> newNotifications = getApolloConfigNotifications(namespaces, clientSideNotifications, watchedKeysMap, latestReleaseMessages);
if (!CollectionUtils.isEmpty(newNotifications)) {
deferredResultWrapper.setResult(newNotifications);
}
return deferredResultWrapper.getResult();
}
use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.
the class NotificationControllerV2 method filterNotifications.
private Map<String, ApolloConfigNotification> filterNotifications(String appId, List<ApolloConfigNotification> notifications) {
Map<String, ApolloConfigNotification> filteredNotifications = Maps.newHashMap();
for (ApolloConfigNotification notification : notifications) {
if (Strings.isNullOrEmpty(notification.getNamespaceName())) {
continue;
}
// strip out .properties suffix
String originalNamespace = namespaceUtil.filterNamespaceName(notification.getNamespaceName());
notification.setNamespaceName(originalNamespace);
// fix the character case issue, such as FX.apollo <-> fx.apollo
String normalizedNamespace = namespaceUtil.normalizeNamespace(appId, originalNamespace);
// which means we should record FX.apollo = 1 here and ignore fx.apollo = 2
if (filteredNotifications.containsKey(normalizedNamespace) && filteredNotifications.get(normalizedNamespace).getNotificationId() < notification.getNotificationId()) {
continue;
}
filteredNotifications.put(normalizedNamespace, notification);
}
return filteredNotifications;
}
use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.
the class NotificationController method handleMessage.
@Override
public void handleMessage(ReleaseMessage message, String channel) {
logger.info("message received - channel: {}, message: {}", channel, message);
String content = message.getMessage();
Tracer.logEvent("Apollo.LongPoll.Messages", content);
if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(content)) {
return;
}
List<String> keys = STRING_SPLITTER.splitToList(content);
// message should be appId+cluster+namespace
if (keys.size() != 3) {
logger.error("message format invalid - {}", content);
return;
}
ResponseEntity<ApolloConfigNotification> notification = new ResponseEntity<>(new ApolloConfigNotification(keys.get(2), message.getId()), HttpStatus.OK);
if (!deferredResults.containsKey(content)) {
return;
}
// create a new list to avoid ConcurrentModificationException
List<DeferredResult<ResponseEntity<ApolloConfigNotification>>> results = Lists.newArrayList(deferredResults.get(content));
logger.debug("Notify {} clients for key {}", results.size(), content);
for (DeferredResult<ResponseEntity<ApolloConfigNotification>> result : results) {
result.setResult(notification);
}
logger.debug("Notification completed");
}
use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.
the class NotificationController method pollNotification.
/**
* For single namespace notification, reserved for older version of apollo clients
*
* @param appId the appId
* @param cluster the cluster
* @param namespace the namespace name
* @param dataCenter the datacenter
* @param notificationId the notification id for the namespace
* @param clientIp the client side ip
* @return a deferred result
*/
@GetMapping
public DeferredResult<ResponseEntity<ApolloConfigNotification>> pollNotification(@RequestParam(value = "appId") String appId, @RequestParam(value = "cluster") String cluster, @RequestParam(value = "namespace", defaultValue = ConfigConsts.NAMESPACE_APPLICATION) String namespace, @RequestParam(value = "dataCenter", required = false) String dataCenter, @RequestParam(value = "notificationId", defaultValue = "-1") long notificationId, @RequestParam(value = "ip", required = false) String clientIp) {
// strip out .properties suffix
namespace = namespaceUtil.filterNamespaceName(namespace);
Set<String> watchedKeys = watchKeysUtil.assembleAllWatchKeys(appId, cluster, namespace, dataCenter);
DeferredResult<ResponseEntity<ApolloConfigNotification>> deferredResult = new DeferredResult<>(TIMEOUT, NOT_MODIFIED_RESPONSE);
// check whether client is out-dated
ReleaseMessage latest = releaseMessageService.findLatestReleaseMessageForMessages(watchedKeys);
/**
* Manually close the entity manager.
* Since for async request, Spring won't do so until the request is finished,
* which is unacceptable since we are doing long polling - means the db connection would be hold
* for a very long time
*/
entityManagerUtil.closeEntityManager();
if (latest != null && latest.getId() != notificationId) {
deferredResult.setResult(new ResponseEntity<>(new ApolloConfigNotification(namespace, latest.getId()), HttpStatus.OK));
} else {
// register all keys
for (String key : watchedKeys) {
this.deferredResults.put(key, deferredResult);
}
deferredResult.onTimeout(() -> logWatchedKeys(watchedKeys, "Apollo.LongPoll.TimeOutKeys"));
deferredResult.onCompletion(() -> {
// unregister all keys
for (String key : watchedKeys) {
deferredResults.remove(key, deferredResult);
}
logWatchedKeys(watchedKeys, "Apollo.LongPoll.CompletedKeys");
});
logWatchedKeys(watchedKeys, "Apollo.LongPoll.RegisteredKeys");
logger.debug("Listening {} from appId: {}, cluster: {}, namespace: {}, datacenter: {}", watchedKeys, appId, cluster, namespace, dataCenter);
}
return deferredResult;
}
use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.
the class NotificationControllerV2Test method testPollNotificationWithIncorrectCase.
@Test
public void testPollNotificationWithIncorrectCase() throws Exception {
String appIdWithIncorrectCase = someAppId.toUpperCase();
String namespaceWithIncorrectCase = defaultNamespace.toUpperCase();
String someMessage = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(someAppId, someCluster, defaultNamespace);
String someWatchKey = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(appIdWithIncorrectCase, someCluster, defaultNamespace);
Multimap<String, String> watchKeysMap = assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey));
String notificationAsString = transformApolloConfigNotificationsToString(defaultNamespace.toUpperCase(), someNotificationId);
when(namespaceUtil.filterNamespaceName(namespaceWithIncorrectCase)).thenReturn(namespaceWithIncorrectCase);
when(namespaceUtil.normalizeNamespace(appIdWithIncorrectCase, namespaceWithIncorrectCase)).thenReturn(defaultNamespace);
when(watchKeysUtil.assembleAllWatchKeys(appIdWithIncorrectCase, someCluster, Sets.newHashSet(defaultNamespace), someDataCenter)).thenReturn(watchKeysMap);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> deferredResult = controller.pollNotification(appIdWithIncorrectCase, someCluster, notificationAsString, someDataCenter, someClientIp);
long someId = 1;
ReleaseMessage someReleaseMessage = new ReleaseMessage(someMessage);
someReleaseMessage.setId(someId);
controller.handleMessage(someReleaseMessage, Topics.APOLLO_RELEASE_TOPIC);
assertTrue(deferredResult.hasResult());
ResponseEntity<List<ApolloConfigNotification>> response = (ResponseEntity<List<ApolloConfigNotification>>) deferredResult.getResult();
assertEquals(1, response.getBody().size());
ApolloConfigNotification notification = response.getBody().get(0);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(namespaceWithIncorrectCase, notification.getNamespaceName());
assertEquals(someId, notification.getNotificationId());
ApolloNotificationMessages notificationMessages = notification.getMessages();
assertEquals(1, notificationMessages.getDetails().size());
assertEquals(someId, notificationMessages.get(someMessage).longValue());
}
Aggregations