import ballerina/http;
import ballerina/io;
import ballerina/runtime;
import ballerina/websub;public function main() {
    io:println("Starting up the Ballerina Hub Service");    websub:Hub webSubHub;
    var result = websub:startHub(new http:Listener(9191), "/websub", "/hub");
    if (result is websub:Hub) {
        webSubHub = result;
    } else if (result is websub:HubStartedUpError) {
        webSubHub = result.startedUpHub;
    } else {
        io:println("Hub start error:" + <string>result.detail()?.message);
        return;
    }
    var registrationResponse = webSubHub.registerTopic(
                                            "http://websubpubtopic.com");
    if (registrationResponse is error) {
        io:println("Error occurred registering topic: " +
                                <string>registrationResponse.detail()?.message);
    } else {
        io:println("Topic registration successful!");
    }
    runtime:sleep(5000);
    io:println("Publishing update to internal Hub");
    var publishResponse = webSubHub.publishUpdate("http://websubpubtopic.com",
        {"action": "publish", "mode": "internal-hub"});    if (publishResponse is error) {
        io:println("Error notifying hub: " +
                                <string>publishResponse.detail()?.message);
    } else {
        io:println("Update notification successful!");
    }
    runtime:sleep(2000);
}
import ballerina/http;
import ballerina/log;
import ballerina/websub;
listener websub:Listener websubEP = new websub:Listener(8181);
@websub:SubscriberServiceConfig {
    path: "/websub",
    subscribeOnStartUp: true,
    target: ["http://localhost:9191/websub/hub", "http://websubpubtopic.com"],
    leaseSeconds: 36000,
    secret: "Kslk30SNF2AChs2"
}
service websubSubscriber on websubEP {
    resource function onIntentVerification(websub:Caller caller,
                                   websub:IntentVerificationRequest request) {
        http:Response response =
            request.buildSubscriptionVerificationResponse("http://websubpubtopic.com");
        if (response.statusCode == 202) {
            log:printInfo("Intent verified for subscription request");
        } else {
            log:printWarn("Intent verification denied for subscription request");
        }
        var result = caller->respond(<@untainted>response);        if (result is error) {
            log:printError("Error responding to intent verification request",
                                                    err = result);
        }
    }
    resource function onNotification(websub:Notification notification) {
        var payload = notification.getTextPayload();
        if (payload is string) {
            log:printInfo("WebSub Notification Received: " + payload);
        } else {
            log:printError("Error retrieving payload as string", payload);
        }
    }
}

Internal Hub Sample

Ballerina provides the capability to easily introduce publishers and subscribers that are WebSub-compliant. Ballerina WebSub subscribers can specify the topic they wish to subscribe to and the hub they wish to subscribe at, to receive notifications. If specified, the subscription process will be initiated at the startup. Signature validation is performed by default for subscribers that receive authenticated content. Ballerina WebSub Subscriber Services could thus be registered as webhooks to receive event notifications.

Ballerina also comes with an in-built WebSub Hub service, which can be brought up by publishers that need to bring up a hub.

In this example, a WebSub Publisher brings up an internal hub and publishes updates to it, and a WebSub Subscriber subscribes at the publisher’s hub to receive notifications of the updates sent to the topic.

import ballerina/http;
import ballerina/io;
import ballerina/runtime;
import ballerina/websub;

The Ballerina WebSub Publisher brings up the internal Ballerina hub, registers a topic at the hub, and publishes updates to the topic.

public function main() {
    io:println("Starting up the Ballerina Hub Service");

Specifies the port that the internal Ballerina hub needs to start on and start the hub.

    websub:Hub webSubHub;
    var result = websub:startHub(new http:Listener(9191), "/websub", "/hub");
    if (result is websub:Hub) {
        webSubHub = result;
    } else if (result is websub:HubStartedUpError) {
        webSubHub = result.startedUpHub;
    } else {
        io:println("Hub start error:" + <string>result.detail()?.message);
        return;
    }
    var registrationResponse = webSubHub.registerTopic(
                                            "http://websubpubtopic.com");
    if (registrationResponse is error) {
        io:println("Error occurred registering topic: " +
                                <string>registrationResponse.detail()?.message);
    } else {
        io:println("Topic registration successful!");
    }

Registers a topic at the hub.

    runtime:sleep(5000);

Makes the publisher wait until the subscriber subscribes at the hub.

    io:println("Publishing update to internal Hub");
    var publishResponse = webSubHub.publishUpdate("http://websubpubtopic.com",
        {"action": "publish", "mode": "internal-hub"});

Publishes directly to the internal Ballerina hub.

    if (publishResponse is error) {
        io:println("Error notifying hub: " +
                                <string>publishResponse.detail()?.message);
    } else {
        io:println("Update notification successful!");
    }
    runtime:sleep(2000);
}

Keeps the service is running until the subscriber receives the update notification.

# This sample requires starting up the Subscriber Service after the Publisher starts up the hub and registers the topic.
# To run this sample, navigate to the directory that contains the
# `.bal` file, and execute the `ballerina run` command.
$ ballerina run publisher.bal
Starting up the Ballerina Hub Service
[ballerina/websub] Ballerina WebSub Hub started up.
[ballerina/websub] Publish URL: http://localhost:9191/websub/publish
[ballerina/websub] Subscription URL: http://localhost:9191/websub/hub
Topic registration successful!
[ballerina/http] started HTTP/WS listener 0.0.0.0:9191
2019-11-01 14:12:22,809 INFO  [ballerina/websub] - Subscription request received for topic[http://websubpubtopic.com] with callback[http://localhost:8181/websub]
2019-11-01 14:12:22,906 INFO  [ballerina/websub] - Sending intent verification request to callback[http://localhost:8181/websub] for topic[http://websubpubtopic.com]
2019-11-01 14:12:23,139 INFO  [ballerina/websub] - Intent verification successful for mode: [subscribe], for callback URL: [http://localhost:8181/websub]
Publishing update to internal Hub
Update notification successful!
import ballerina/http;
import ballerina/log;
import ballerina/websub;

Ballerina WebSub Subscriber service, which subscribes to notifications at a Hub.

listener websub:Listener websubEP = new websub:Listener(8181);

The endpoint to which the subscriber service is bound.

@websub:SubscriberServiceConfig {
    path: "/websub",
    subscribeOnStartUp: true,
    target: ["http://localhost:9191/websub/hub", "http://websubpubtopic.com"],
    leaseSeconds: 36000,
    secret: "Kslk30SNF2AChs2"
}
service websubSubscriber on websubEP {

Annotations specifying the subscription parameters.

    resource function onIntentVerification(websub:Caller caller,
                                   websub:IntentVerificationRequest request) {

Define sthe resource, which accepts the intent verification requests. If the resource is not specified, intent verification happens automatically. It verifies if the topic specified in the intent verification request matches the topic specified as the annotation.

        http:Response response =
            request.buildSubscriptionVerificationResponse("http://websubpubtopic.com");
        if (response.statusCode == 202) {
            log:printInfo("Intent verified for subscription request");
        } else {
            log:printWarn("Intent verification denied for subscription request");
        }
        var result = caller->respond(<@untainted>response);

Builds the response to the intent verification request that was received for subscription.

        if (result is error) {
            log:printError("Error responding to intent verification request",
                                                    err = result);
        }
    }
    resource function onNotification(websub:Notification notification) {
        var payload = notification.getTextPayload();
        if (payload is string) {
            log:printInfo("WebSub Notification Received: " + payload);
        } else {
            log:printError("Error retrieving payload as string", payload);
        }
    }
}

Defines the resource that accepts the content delivery requests.

# To start the service, navigate to the directory that contains the
# `.bal` file, and use the `ballerina run` command.
$ ballerina run subscriber.bal
[ballerina/http] started HTTP/WS listener 0.0.0.0:8181
2019-11-01 14:12:22,870 INFO  [ballerina/websub] - Subscription Request successfully sent to Hub[http://localhost:9191/websub/hub], for Topic[http://websubpubtopic.com], with Callback [http://localhost:8181/websub]
2019-11-01 14:12:23,074 INFO  [] - Intent verified for subscription request
2019-11-01 14:12:25,035 INFO  [] - WebSub Notification Received: {"action":"publish", "mode":"internal-hub"}