Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -377,23 +377,14 @@
<configuration>
<!-- Exclude classes generated from proto3 files -->
<excludes>
<exclude>org/eclipse/uprotocol/Uoptions.*</exclude>
<exclude>org/eclipse/uprotocol/UServiceTopic*</exclude>
<exclude>org/eclipse/uprotocol/core/**</exclude>
<exclude>org/eclipse/uprotocol/v1/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
<configuration>
<includes>
<include>**/client/**</include>
<include>**/cloudevent/**</include>
<include>**/communication/**</include>
<include>**/transport/**</include>
<include>**/uri/**</include>
<include>**/uuid/**</include>
<include>**/validation/**</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public final class UMessageBuilder {

/**
* Gets a builder for a publish message.
*
* <p>
* A publish message is used to notify all interested consumers of an event that has occurred.
* Consumers usually indicate their interest by <em>subscribing</em> to a particular topic.
*
Expand All @@ -75,7 +75,7 @@ public static UMessageBuilder publish(UUri source) {

/**
* Gets a builder for a notification message.
*
* <p>
* A notification is used to inform a specific consumer about an event that has occurred.
*
* @param source The component that the notification originates from.
Expand Down Expand Up @@ -135,13 +135,6 @@ public static UMessageBuilder request(UUri source, UUri sink, int ttl) {
* <p>
* The builder will be initialized with {@link UPriority#UPRIORITY_CS4}.
*
* # Arguments
*
* * `reply_to_address` - The URI that the sender of the request expects to receive the response message at.
* * `request_id` - The identifier of the request that this is the response to.
* * `invoked_method` - The URI identifying the method that has been invoked and which the created message is
* the outcome of.
*
* @param source The URI identifying the method that has been invoked and which the created message is
* the outcome of.
* @param sink The URI that the sender of the request expects to receive the response message at.
Expand Down Expand Up @@ -169,16 +162,9 @@ public static UMessageBuilder response(UUri source, UUri sink, UUID reqid) {
* <p>
* A response message is used to send the outcome of processing a request message
* to the original sender of the request message.
* <p>
* The builder will be initialized with values from the given request attributes.
*
* # Arguments
*
* * `request_attributes` - The attributes from the request message. The response message
* builder will be initialized with the corresponding attribute values.
*
* @param request The attributes from the request message. The response message
* builder will be initialized with the corresponding attribute values.
* builder will be initialized with the corresponding attribute values.
* @return The builder.
* @throws NullPointerException if request is {@code null}.
* @throws IllegalArgumentException if the request does not contain valid request attributes.
Expand Down Expand Up @@ -219,7 +205,7 @@ private UMessageBuilder(UUri source, UUID id, UMessageType type) {

/**
* Sets the message's identifier.
*
* <p>
* Every message must have an identifier. If this method is not used, an identifier will be
* generated and set on the message when one of the <em>build</em> methods is invoked.
*
Expand All @@ -241,14 +227,11 @@ public UMessageBuilder withMessageId(UUID id) {
/**
* Sets the message's time-to-live.
*
* @param ttl The time-to-live in milliseconds.
* @param ttl The time-to-live in milliseconds. Note that the value is interpreted as an
* <em>unsigned</em> integer. A value of 0 indicates that the message never expires.
* @return The builder with the configured ttl.
* @throws IllegalArgumentException if the ttl is negative.
*/
public UMessageBuilder withTtl(int ttl) {
if (ttl < 0) {
throw new IllegalArgumentException("TTL must be a non-negative integer.");
}
this.ttl = ttl;
return this;
}
Expand Down Expand Up @@ -296,16 +279,12 @@ public UMessageBuilder withPriority(UPriority priority) {
/**
* Sets the message's permission level.
*
* @param plevel The level.
* @param plevel The level. Note that the value is interpreted as an <em>unsigned</em> integer.
* @return The builder with the configured permission level.
* @throws IllegalArgumentException if the permission level is less than 0.
* @throws IllegalStateException if the message is not an RPC request message.
*/
public UMessageBuilder withPermissionLevel(int plevel) {
// [impl->dsn~up-attributes-permission-level~1]
if (plevel < 0) {
throw new IllegalArgumentException("Permission level must be greater than or equal to 0.");
}
if (this.type != UMessageType.UMESSAGE_TYPE_REQUEST) {
throw new IllegalStateException("Permission level can only be set for RPC request messages.");
}
Expand Down Expand Up @@ -372,11 +351,6 @@ public UMessage build(UPayload payload) {
/**
* Creates the message based on the builder's state.
*
* # Errors
*
* If the properties set on the builder do not represent a consistent set of [`UAttributes`],
* a [`UMessageError::AttributesValidationError`] is returned.
*
* @return A message ready to be sent using a transport implementation.
* @throws ValidationException if the properties set on the builder do not represent a
* consistent set of attributes as determined by {@link UAttributesValidator#validate(UAttributes)}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,42 @@ public final void validateRpcPriority(UAttributes attributes) {
}
}

/**
* Validates the time-to-live configuration of RPC messages.
*
* @param attributes The attributes to check.
* @throws IllegalArgumentException if the attributes do not represent an RPC message.
* @throws ValidationException if the attributes do not contain a TTL or if its value is 0.
*/
public final void validateRpcTtl(UAttributes attributes) {
if (attributes.getType() != UMessageType.UMESSAGE_TYPE_REQUEST
&& attributes.getType() != UMessageType.UMESSAGE_TYPE_RESPONSE) {
throw new IllegalArgumentException("Attributes do not represent an RPC message");
}
if (!attributes.hasTtl()) {
throw new ValidationException("RPC messages must contain a TTL");
}
int ttl = attributes.getTtl();
// TTL is interpreted as an unsigned integer, so negative values are not possible
if (ttl == 0) {
throw new ValidationException("RPC message's TTL must not be 0");
}
}

/**
* Checks if a given set of attributes belong to a message that has expired.
* <p>
* The message is considered expired if the message's creation time plus the
* duration indicated by the <em>ttl</em> attribute is <em>before</em> the current
* instant in time.
* instant in (system) time.
*
* @param attributes The attributes to check.
* @return {@code true} if the given attributes should be considered expired.
*/
public final boolean isExpired(UAttributes attributes) {
final int ttl = attributes.getTtl();
return ttl > 0 && UuidUtils.isExpired(attributes.getId(), ttl, Instant.now());
// TTL is interpreted as an unsigned integer, so negative values are not possible
return Integer.compareUnsigned(ttl, 0) > 0 && UuidUtils.isExpired(attributes.getId(), ttl, Instant.now());
}

/*
Expand Down Expand Up @@ -178,43 +201,6 @@ public final boolean isExpired(UAttributes attributes) {
*/
public abstract void validate(UAttributes attributes);

/**
* Validates the time-to-live configuration.
* <p>
* If the UAttributes does not contain a time to live then the ValidationResult is ok.
*
* @param attributes The attributes to check.
* @throws ValidationException if the attributes contain a negative TTL.
*/
public void validateTtl(UAttributes attributes) {
if (!attributes.hasTtl()) {
return;
}
int ttl = attributes.getTtl();
if (ttl < 0) {
throw new ValidationException(String.format("TTL must be a non-negative integer [%s]", ttl));
}
}

/**
* Validate the permissionLevel for the default case. If the UAttributes does
* not contain a permission level then
* the ValidationResult is ok.
*
* @param attributes The attributes to check.
* @throws ValidationException if the attributes contain a negative permission level.
*/
public final void validatePermissionLevel(UAttributes attributes) {
if (!attributes.hasPermissionLevel()) {
return;
}
final var level = attributes.getPermissionLevel();
if (level < 0) {
throw new ValidationException(
String.format("Permission level must be a non-negative integer [%d]", level));
}
}

/**
* Validators for the message types defined by uProtocol.
*/
Expand Down Expand Up @@ -294,7 +280,6 @@ public void validate(UAttributes attributes) {
this::validateId,
this::validateSource,
this::validateSink,
this::validateTtl,
this::validatePriority
);
if (!errors.isEmpty()) {
Expand Down Expand Up @@ -366,7 +351,6 @@ public void validate(UAttributes attributes) {
this::validateId,
this::validateSource,
this::validateSink,
this::validateTtl,
this::validatePriority
);
if (!errors.isEmpty()) {
Expand Down Expand Up @@ -435,37 +419,15 @@ public void validateSink(UAttributes attributes) {
}
}

/**
* Verifies that a set of attributes representing an RPC request contain a valid time-to-live.
*
* @param attributes The attributes to check.
* @throws ValidationException if the attributes do not contain a time-to-live,
* or if the time-to-live is &lt;= 0.
*/
@Override
public void validateTtl(UAttributes attributes) {
// [impl->dsn~up-attributes-request-ttl~1]
if (!attributes.hasTtl()) {
throw new ValidationException("RPC request message must contain a TTL");
}
int ttl = attributes.getTtl();
if (ttl <= 0) {
throw new ValidationException(String.format(
"RPC request message's TTL must be a positive integer [%d]",
ttl));
}
}

@Override
public void validate(UAttributes attributes) {
final var errors = ValidationUtils.collectErrors(attributes,
this::validateType,
this::validateId,
this::validateSource,
this::validateSink,
this::validateTtl,
this::validateRpcPriority,
this::validatePermissionLevel
this::validateRpcTtl,
this::validateRpcPriority
);
if (!errors.isEmpty()) {
throw new ValidationException(errors);
Expand Down Expand Up @@ -575,7 +537,7 @@ public void validate(UAttributes attributes) {
this::validateSource,
this::validateSink,
this::validateReqId,
this::validateTtl,
this::validateRpcTtl,
this::validateRpcPriority,
this::validateCommstatus
);
Expand Down
30 changes: 11 additions & 19 deletions src/main/java/org/eclipse/uprotocol/uuid/factory/UuidUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static boolean isUProtocol(UUID uuid) {
* @throws NullPointerException if the UUID is {@code null}.
* @throws IllegalArgumentException if the UUID is not a uProtocol UUID.
*/
public static long getTime(UUID uuid) {
public static long getTimestamp(UUID uuid) {
Objects.requireNonNull(uuid);
if (!isUProtocol(uuid)) {
throw new IllegalArgumentException("UUID is not a uProtocol UUID");
Expand All @@ -101,7 +101,7 @@ public static long getElapsedTime(UUID id, Instant now) {
if (!isUProtocol(id)) {
throw new IllegalArgumentException("UUID is not a uProtocol UUID");
}
final var creationTime = getTime(id);
final var creationTime = getTimestamp(id);
final var referenceTime = now != null ? now.toEpochMilli() : Instant.now().toEpochMilli();
return referenceTime - creationTime;
}
Expand All @@ -118,21 +118,16 @@ public static long getElapsedTime(UUID id, Instant now) {
* has already expired.
* @throws NullPointerException if the UUID is {@code null}.
* @throws IllegalArgumentException if the UUID is not a uProtocol UUID.
* @throws IllegalArgumentException if the TTL is non-positive.
* @throws IllegalArgumentException if the TTL is 0.
*/
public static long getRemainingTime(UUID id, int ttl, Instant now) {
Objects.requireNonNull(id);
if (!isUProtocol(id)) {
throw new IllegalArgumentException("UUID is not a uProtocol UUID");
}
if (ttl <= 0) {
throw new IllegalArgumentException("TTL must be positive");
if (ttl == 0) {
throw new IllegalArgumentException("TTL must not be 0");
}
final var creationTime = getTime(id);
final var referenceTime = now != null ? now.toEpochMilli() : Instant.now().toEpochMilli();
final var elapsedTime = referenceTime - creationTime;
if (elapsedTime >= ttl) {
return 0;
final long elapsedTime = getElapsedTime(id, now);
if (Long.compareUnsigned(elapsedTime, ttl) > 0) {
return 0; // Expired
} else {
return ttl - elapsedTime;
}
Expand All @@ -142,22 +137,19 @@ public static long getRemainingTime(UUID id, int ttl, Instant now) {
* Checks if an object identified by a given UUID should be considered expired.
*
* @param id The UUID.
* @param ttl The object's time-to-live (TTL) in milliseconds.
* @param ttl The object's time-to-live (TTL) in milliseconds. Note that the TTL is
* interpreted as an <em>unsigned</em> integer.
* @param now The reference point in time that the calculation should be based on, given
* as the number of milliseconds since the Unix epoch, or {@code null} to use the current point in time.
* @return {@code true} if the object's TTL has already expired.
* @throws NullPointerException if the UUID is {@code null}.
* @throws IllegalArgumentException if the UUID is not a uProtocol UUID.
* @throws IllegalArgumentException if the TTL is negative.
*/
public static boolean isExpired(UUID id, int ttl, Instant now) {
Objects.requireNonNull(id);
if (!isUProtocol(id)) {
throw new IllegalArgumentException("UUID is not a uProtocol UUID");
}
if (ttl < 0) {
throw new IllegalArgumentException("TTL must be non-negative");
}
return ttl > 0 && getRemainingTime(id, ttl, now) == 0;
return Integer.compareUnsigned(ttl, 0) > 0 && getRemainingTime(id, ttl, now) == 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ void testHandleUnexpectedResponse(Consumer<UMessage> unexpectedMessageHandler) {
METHOD_URI,
TRANSPORT_SOURCE,
reqId)
.withTtl(54321)
.build();
responseListener.getValue().onReceive(responseMessage);

Expand Down
Loading