Skip to content

Commit

Permalink
semantic enrichment of real-time notification utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian-Winter-Software-AG committed Dec 18, 2024
1 parent 0b12e6a commit ceefe21
Showing 1 changed file with 67 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package com.cumulocity.sdk.client.notification;

import java.util.HashMap;

import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.Map;

import com.cumulocity.model.idtype.GId;
import com.cumulocity.sdk.client.PlatformParameters;
import com.cumulocity.rest.representation.AbstractExtensibleRepresentation;
import com.cumulocity.rest.representation.alarm.AlarmRepresentation;
import com.cumulocity.rest.representation.event.EventRepresentation;
import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation;
import com.cumulocity.rest.representation.measurement.MeasurementRepresentation;
import com.cumulocity.rest.representation.notification.NotificationRepresentation;
import com.cumulocity.rest.representation.notification.NotificationRepresentation.*;
import com.cumulocity.sdk.client.notification.Subscriber;
import com.cumulocity.sdk.client.notification.SubscriberBuilder;
import com.cumulocity.rest.representation.operation.OperationRepresentation;
import com.google.common.collect.ImmutableMap;

/**
* Utility class for creating {@linkplain Subscriber subscribers} to the real-time
* notification API.
*
* This class is used as follows (example):
* <pre>{@code
* // 'parameters' object of type PlatformParameters must be provided
* NotificationSubscriberProducer producer = new NotificationSubscriberProducer(parameters);
* producer.getSubscriber(Endpoint.RealtimeNotifications, AlarmNotificationRepresentation.class)
* producer.getSubscriber(Endpoint.RealtimeNotifications, NotificationType.ALARM)
* .subscribe(new GId("*"), new SubscriptionListener<>() { ... });
* }</pre>
*
Expand All @@ -35,6 +37,37 @@
*/
public class NotificationSubscriberProducer {

/**
* This utility class has a constant for each domain model object type
* that supports notifications. These constants are used as inputs
* to some methods in the context of the enclosing class.
*
* @param <S> the type of domain model object
* @param <T> the type of notification object corresponding to
* the domain model object type
*/
// This could be an Enum, if JEP 301 (enhanced enums with sharper typing) was available.
public static final class NotificationType<S extends AbstractExtensibleRepresentation, T extends NotificationRepresentation<S>> {
public static final NotificationType<ManagedObjectRepresentation, ManagedObjectNotificationRepresentation>
MANAGED_OBJECT = new NotificationType<>(ManagedObjectRepresentation.class, ManagedObjectNotificationRepresentation.class);
public static final NotificationType<MeasurementRepresentation, MeasurementNotificationRepresentation>
MEASUREMENT = new NotificationType<>(MeasurementRepresentation.class, MeasurementNotificationRepresentation.class);
public static final NotificationType<EventRepresentation, EventNotificationRepresentation>
EVENT = new NotificationType<>(EventRepresentation.class, EventNotificationRepresentation.class);
public static final NotificationType<AlarmRepresentation, AlarmNotificationRepresentation>
ALARM = new NotificationType<>(AlarmRepresentation.class, AlarmNotificationRepresentation.class);
public static final NotificationType<OperationRepresentation, OperationNotificationRepresentation>
OPERATION = new NotificationType<>(OperationRepresentation.class, OperationNotificationRepresentation.class);

public final Class<S> resourceRepresentation;
public final Class<T> notificationRepresentation;

private NotificationType(Class<S> resourceRepresentation, Class<T> notificationRepresentation) {
this.resourceRepresentation = resourceRepresentation;
this.notificationRepresentation = notificationRepresentation;
}
}

/**
* Enumeration of the available real-time notification endpoints.
* <ul>
Expand All @@ -44,56 +77,46 @@ public class NotificationSubscriberProducer {
*/
public enum Endpoint {
/**
* Generic real-time notification endpoint.
* Generic real-time notification endpoint “{@code notification/realtime}”.
* This endpoint is usable for receiving notifications for any type of
* domain model objects (Measurements, Events, etc.) that supports
* real-time notifications.
*/
RealtimeNotifications("notification/realtime"),
RealtimeNotifications("notification/realtime",
ImmutableMap.of(
NotificationType.MANAGED_OBJECT, "/managedobjects/",
NotificationType.MEASUREMENT, "/measurements/",
NotificationType.EVENT, "/events/",
NotificationType.ALARM, "/alarms/",
NotificationType.OPERATION, "/operations/")),

/**
* Special real-time notification endpoint for Operations.
* Real-time notification endpoint “{@code notification/operations}”.
* Use this endpoint for receiving operations for Agents (i.&thinsp;e.
* Managed Objects having a {@code com_cumulocity_model_Agent} fragment).
* For receiving operations for regular Devices (having a {@code c8y_IsDevice}
* fragment), use the {@link #RealtimeNotifications} endpoint instead.
*/
OperationNotifications("notification/operations");
OperationNotifications("notification/operations",
ImmutableMap.of(NotificationType.OPERATION, "/"));

/**
* The URL path of this endpoint.
*/
public final String path;

Endpoint(String path) {
/**
* This map provides the prefix of subscription channels (like {@code "/alarms/"})
* for each supported data type.
*/
public final Map<NotificationType<?,?>, String> supportedChannelPrefixes;

Endpoint(String path, Map<NotificationType<?,?>, String> prefixes) {
this.path = path;
this.supportedChannelPrefixes = prefixes;
}
}

private static final HashMap<
ImmutablePair<Endpoint, Class<? extends NotificationRepresentation<?>>>,
String /*subscriptionBase*/>
subscriptionBaseMap = new HashMap<>();
static {
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.RealtimeNotifications, ManagedObjectNotificationRepresentation.class),
"/managedobjects/");
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.RealtimeNotifications, MeasurementNotificationRepresentation.class),
"/measurements/");
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.RealtimeNotifications, EventNotificationRepresentation.class),
"/events/");
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.RealtimeNotifications, AlarmNotificationRepresentation.class),
"/alarms/");
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.RealtimeNotifications, OperationNotificationRepresentation.class),
"/operations/");
subscriptionBaseMap.put(
new ImmutablePair<>(Endpoint.OperationNotifications, OperationNotificationRepresentation.class),
"/");
}

private final PlatformParameters parameters;

/**
Expand All @@ -107,7 +130,7 @@ public NotificationSubscriberProducer(PlatformParameters parameters) {
}

/**
* Create a {@link Subscriber} to the given endpoint for receiving real-time
* Creates a subscriber to the given endpoint for receiving real-time
* notifications of the given data type.
*
* The data type depends on the type of domain model objects for which
Expand All @@ -122,20 +145,20 @@ public NotificationSubscriberProducer(PlatformParameters parameters) {
*
* @param <T> the desired data type as indicated by the {@code type} parameter
* @param endpoint the desired real-time notification endpoint
* @param type the class of the desired data type
* @param type the desired type of notification
* @return a subscriber with the specified configuration
*/
public <T extends NotificationRepresentation<?>>
Subscriber<GId, T> getSubscriber(Endpoint endpoint, Class<T> type) {
Subscriber<GId, T> getSubscriber(Endpoint endpoint, NotificationType<?,T> type) {

String subscriptionBase = subscriptionBaseMap.get(new ImmutablePair<>(endpoint, type));
if (subscriptionBase == null)
String channelPrefix = endpoint.supportedChannelPrefixes.get(type);
if (channelPrefix == null)
throw new IllegalArgumentException("Invalid combination of endpoint and type.");
return new SubscriberBuilder<GId, T>()
.withParameters(parameters)
.withEndpoint(endpoint.path)
.withSubscriptionNameResolver(id -> subscriptionBase + id.getValue())
.withDataType(type)
.withSubscriptionNameResolver(id -> channelPrefix + id.getValue())
.withDataType(type.notificationRepresentation)
.withMessageDeliveryAcknowlage(true)
.build();
}
Expand Down

0 comments on commit ceefe21

Please sign in to comment.