private void startExtensibilityRegistry() { extensibilityRegistry = new ExtensibilitySubscriptionManager(); this.startService(extensibilityRegistry); }
protected ExtensibilitySubscription getExtensibilitySubscription(TaskServiceDocument<?> task) { return subscriptions.get(constructKey(task)); }
<T extends TaskServiceDocument<?>> void handleStagePatch(T state, Consumer<T> callback, Runnable notificationCallback) { ExtensibilitySubscription extensibilitySubscription = getExtensibilitySubscription(state); if (extensibilitySubscription == null) { // no extensibility subscription registered, continue task execution callback.accept(state); return; } notificationCallback.run(); }
private DeferredResult<Void> subscribeForSubscriptions() { DeferredResult<Void> subscriptionsRead = new DeferredResult<Void>(); CommonContinuousQueries .subscribeTo(this.getHost(), ContinuousQueryId.EXTENSIBILITY_SUBSCRIPTIONS, op -> onSubscriptionChange(op, subscriptionsRead)); return subscriptionsRead; }
private DeferredResult<Void> subscribeForTopics() { DeferredResult<Void> topicsRead = new DeferredResult<Void>(); CommonContinuousQueries.subscribeTo(this.getHost(), ContinuousQueryId.EVENT_TOPICS, op -> onEventTopicChange(op, topicsRead)); return topicsRead; }
ExtensibilitySubscription extensibility, T state) { logFine("Sending blocking notification to [%s] for [%s]", extensibility.callbackReference, state.documentSelfLink); callbackState.tenantLinks = state.tenantLinks; Duration stagePhaseTimeout = getStagePhaseTimeout(state); sendRequest(Operation .createPost(this, ExtensibilitySubscriptionCallbackService.FACTORY_LINK) .setBody(callbackState) .setCompletion((o, e) -> { if (e != null) { logSevere("Failure creating extensibility callback: %s", Utils.toJson(e)); return; .getBody(ExtensibilitySubscriptionCallback.class); sendExternalNotification(extensibility, buildDataToSend(notificationPayload, replyPayload, result), state, NOTIFICATION_RETRY_COUNT); }));
int retriesLeft) { sendRequest(Operation.createPost(extensibility.callbackReference) .setBody(body) .setCompletion((o, e) -> { if (e != null) { logWarning( "Retrying [%s] times to notify [%s]. Error: [%s]", retriesLeft, extensibility.callbackReference, logWarning("Cannot notify [%s] for task [%s]. Error: %s", extensibility.callbackReference, body.documentSelfLink, e.getMessage()); failTask(e.getMessage(), state.documentSelfLink); } else if (o.getStatusCode() == Operation.STATUS_CODE_TIMEOUT) { logWarning("Request to [%s] for task [%s] expired!", extensibility.callbackReference, body.documentSelfLink); } else { getHost().schedule(() -> { sendExternalNotification(extensibility, body, state, retriesLeft - 1); }, NOTIFICATION_RETRY_WAIT, TimeUnit.SECONDS);
private DeferredResult<Void> loadTopics() { DeferredResult<Void> res = new DeferredResult<>(); QueryTask q = QueryUtil.buildQuery(EventTopicState.class, false); QueryUtil.addExpandOption(q); new ServiceDocumentQuery<>(getHost(), EventTopicState.class) .query(q, (r) -> { if (r.hasException()) { logSevere("Exception while initializing LifecycleExtensibilityManager. " + "Error: [%s]", r.getException().getMessage()); res.fail(r.getException()); } else if (r.hasResult()) { EventTopicState state = r.getResult(); handleUpdateEventTopicState(state); } else { logInfo("Loaded %d extensibility states", subscriptions.size()); res.complete(null); } }); return res; }
private DeferredResult<Void> loadSubscriptions() { DeferredResult<Void> res = new DeferredResult<>(); QueryTask q = QueryUtil.buildQuery(ExtensibilitySubscription.class, false); QueryUtil.addExpandOption(q); new ServiceDocumentQuery<>(getHost(), ExtensibilitySubscription.class) .query(q, (r) -> { if (r.hasException()) { logSevere("Exception while initializing LifecycleExtensibilityManager. " + "Error: [%s]", r.getException().getMessage()); res.fail(r.getException()); } else if (r.hasResult()) { ExtensibilitySubscription state = r.getResult(); addExtensibilitySubscription(state); } else { logInfo("Loaded %d extensibility states", subscriptions.size()); res.complete(null); } }); return res; }
private void failTask(String msg, String taskDocumentSelfLink) { String errMsg = msg != null ? msg : "Unexpected State"; logWarning("Fail extensibility task: %s", errMsg); ServiceTaskCallbackResponse body = new ServiceTaskCallbackResponse(); body.taskInfo = new TaskState(); body.taskInfo.stage = TaskStage.FAILED; body.taskSubStage = DefaultSubStage.ERROR; ServiceErrorResponse rsp = new ServiceErrorResponse(); rsp.message = errMsg; body.taskInfo.failure = rsp; sendRequest(Operation.createPatch(UriUtils.buildUri(getHost(), taskDocumentSelfLink)) .setBody(body) .setCompletion((op, ex) -> { if (ex != null) { logWarning("Patch for fail task operation failed: %s", Utils.toString(ex)); } })); }
private void handleSubscriptions(T state) { // Check if Task allows subscription on this stage & prevent sending of event more than once if (subscriptionSubStages.contains(state.taskSubStage) && (state.customProperties == null || !state.customProperties.containsKey(constructExtensibilityResponseKey (state))) && !skipExtensibility(state)) { ExtensibilitySubscriptionManager manager = getExtensibilityManager(); if (manager != null) { BaseExtensibilityCallbackResponse notificationPayload = this.notificationPayload (state); //Once payload being enhanced, manager will sent notification to client. Runnable notificationCallback = () -> { // Callback will trigger notification call to client. Runnable callback = () -> { manager.sendNotification(manager.getExtensibilitySubscription(state), notificationPayload, this.replyPayload(state), state, this::handleStagePatch); }; this.validateAndEnhanceNotificationPayload(state, notificationPayload, callback); }; manager.handleStagePatch(state, this::handleStagePatch, notificationCallback); } else { // ServiceHost is not instance of ManagementHost handleStagePatch(state); } } else { // Task doesn't allow subscription on current stage. handleStagePatch(state); } }
private void onSubscriptionChange(Operation op, DeferredResult<Void> done) { op.complete(); QueryTask queryTask = op.getBody(QueryTask.class); if (queryTask.results != null) { for (ExtensibilitySubscription subscription : queryTask.results.documents.values() .stream().map(o -> o instanceof JsonObject ? Utils.fromJson(o, ExtensibilitySubscription.class) : (ExtensibilitySubscription) o) .collect(Collectors.toList())) { if (Action.DELETE.toString().equals(subscription.documentUpdateAction)) { removeExtensibilitySubscription(subscription.documentSelfLink); } else { addExtensibilitySubscription(subscription); } } done.complete(null); } }
@SuppressWarnings("rawtypes") private <T extends TaskServiceDocument> ServiceDocument buildDataToSend( ServiceTaskCallbackResponse notificationPayload, ServiceTaskCallbackResponse replyPayload, ExtensibilitySubscriptionCallback result) { // Notification payload will give information about the task to subscriber. ServiceTaskCallbackResponse notificationPayloadData = Utils.fromJson( result.taskStateJson, notificationPayload.getClass()); //Copy enhanced payload (if some enhancements to payload have been made) PropertyUtils.mergeObjects(notificationPayload, notificationPayloadData, PropertyUtils.SHALLOW_MERGE_STRATEGY); notificationPayload.customProperties = notificationPayload .customProperties != null ? filterSystemProperties(notificationPayload .customProperties) : null; // Get service reply payload in order to notify subscriber which fields are acceptable for // response. ServiceTaskCallbackResponse replyPayloadData = Utils.fromJson( result.taskStateJson, replyPayload.getClass()); ExtensibilitySubscriptionCallback data = new ExtensibilitySubscriptionCallback(); data.serviceCallback = UriUtils.buildUri(getHost(), result.documentSelfLink); data.notificationPayload = Utils.toJson(notificationPayload); data.replyPayload = replyPayloadData; data.taskStateClassName = result.taskStateClassName; data.tenantLinks = result.tenantLinks; return data; }
@Before public void setUp() throws Throwable { sender = host.getTestRequestSender(); host.startServiceAndWait(ConfigurationFactoryService.class, ConfigurationFactoryService.SELF_LINK); manager = new ExtensibilitySubscriptionManager(); host.startServiceAndWait(manager, ExtensibilitySubscriptionManager.SELF_LINK, null); host.startServiceAndWait(ExtensibilitySubscriptionFactoryService.class, ExtensibilitySubscriptionFactoryService.SELF_LINK); host.startFactory(new EventTopicService()); waitForServiceAvailability(EventTopicService.FACTORY_LINK); }
private void handleUpdateEventTopicState(EventTopicState state) { String customConfig = System.getProperty(state.id + TIMEOUT_SUFFIX); Duration timeout = customConfig != null ? Duration.parse(customConfig) : EXTENSIBILITY_TIMEOUT; String stateKey = constructKey(state); timeoutsPerTaskStageAndSubstage.put(stateKey, timeout); topicsPerTaskStageAndSubstage.put(stateKey, state.id); }
private <T extends TaskServiceDocument> Duration getStagePhaseTimeout(T state) { String stateSubstateKey = constructKey(state); String topicTimeoutKey = topicsPerTaskStageAndSubstage.get(stateSubstateKey) + TIMEOUT_SUFFIX; if (state.customProperties != null && state.customProperties.containsKey(topicTimeoutKey)) { return Duration.parse(state.customProperties.get(topicTimeoutKey).toString()); } else { return timeoutsPerTaskStageAndSubstage.getOrDefault(stateSubstateKey, EXTENSIBILITY_TIMEOUT); } } }