Skip to content

Commit 1756b95

Browse files
committed
[#180] Change uEntity instance ID wildcard to 0xFFFF_0000
Adopted change to the entity instance ID wildcard pattern made in the UUri specification. Also fixed UriValidator check methods to handle UUris containing wildcard resource IDs consistently. Fixes #179 Fixes #180
1 parent 3d63f25 commit 1756b95

File tree

8 files changed

+676
-960
lines changed

8 files changed

+676
-960
lines changed

src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ public void validateSink(UAttributes attributes) {
443443
if (!attributes.hasSink() || attributes.getSink() == UUri.getDefaultInstance()) {
444444
throw new ValidationException("Missing Sink");
445445
}
446-
if (!UriValidator.isDefaultResourceId(attributes.getSink())) {
446+
if (!UriValidator.isNotificationDestination(attributes.getSink())) {
447447
throw new ValidationException("Invalid Sink Uri");
448448
}
449449
}

src/main/java/org/eclipse/uprotocol/uri/factory/UriFactory.java

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212
*/
1313
package org.eclipse.uprotocol.uri.factory;
1414

15+
16+
import java.util.Objects;
17+
import java.util.Optional;
18+
1519
import org.eclipse.uprotocol.Uoptions;
20+
import org.eclipse.uprotocol.uri.validator.UriValidator;
1621
import org.eclipse.uprotocol.v1.UUri;
1722

1823
import com.google.protobuf.DescriptorProtos.ServiceOptions;
@@ -24,7 +29,9 @@
2429
public final class UriFactory {
2530

2631
public static final String WILDCARD_AUTHORITY = "*";
27-
public static final int WILDCARD_ENTITY_ID = 0xFFFF;
32+
public static final int WILDCARD_ENTITY_TYPE_ID = 0x0000_FFFF;
33+
public static final int WILDCARD_ENTITY_INSTANCE_ID = 0xFFFF_0000;
34+
public static final int WILDCARD_ENTITY_ID = WILDCARD_ENTITY_TYPE_ID | WILDCARD_ENTITY_INSTANCE_ID;
2835
public static final int WILDCARD_ENTITY_VERSION = 0xFF;
2936
public static final int WILDCARD_RESOURCE_ID = 0xFFFF;
3037

@@ -42,39 +49,52 @@ private UriFactory() {
4249
}
4350

4451
/**
45-
* Builds a UEntity for an protobuf generated code Service Descriptor.
46-
*
47-
* @param descriptor The protobuf generated code Service Descriptor.
48-
* @param resourceId The resource id.
49-
* @return Returns a UEntity for an protobuf generated code Service Descriptor.
52+
* Creates a uProtocol URI for a resource defined by a protobuf service descriptor.
53+
* <p>
54+
* The descriptor is expected to contain {@link Uoptions#serviceId service ID}
55+
* and {@link Uoptions#serviceVersionMajor major version} options.
56+
*
57+
* @param descriptor The service descriptor to create the URI for.
58+
* @param resourceId The resource ID to create the URI for.
59+
* @return The URI.
60+
* @throws NullPointerException if the descriptor is {@code null}.
61+
* @throws IllegalArgumentException if the descriptor does not contain the required options
62+
* or if the options can not be used to create a valid uProtocol URI.
5063
*/
5164
public static UUri fromProto(ServiceDescriptor descriptor, int resourceId) {
5265
return fromProto(descriptor, resourceId, null);
5366
}
5467

5568
/**
56-
* Builds a UEntity for an protobuf generated code Service Descriptor.
69+
* Creates a uProtocol URI for a resource defined by a protobuf service descriptor.
70+
* <p>
71+
* The descriptor is expected to contain {@link Uoptions#serviceId service ID}
72+
* and {@link Uoptions#serviceVersionMajor major version} options.
5773
*
58-
* @param descriptor The protobuf generated code Service Descriptor.
59-
* @param resourceId The resource id.
60-
* @param authorityName The authority name.
61-
* @return Returns a UEntity for an protobuf generated code Service Descriptor.
74+
* @param descriptor The service descriptor to create the URI for.
75+
* @param resourceId The resource ID to create the URI for.
76+
* @param authorityName The URI's authority name or {@code null} to create a local URI.
77+
* @return The URI.
78+
* @throws NullPointerException if the descriptor is {@code null}.
79+
* @throws IllegalArgumentException if the descriptor does not contain the required options
80+
* or if the options can not be used to create a valid uProtocol URI.
6281
*/
6382
public static UUri fromProto(ServiceDescriptor descriptor, int resourceId, String authorityName) {
64-
if (descriptor == null) {
65-
return UUri.getDefaultInstance();
66-
}
67-
83+
Objects.requireNonNull(descriptor);
6884
final ServiceOptions options = descriptor.getOptions();
85+
if (!options.hasExtension(Uoptions.serviceId) || !options.hasExtension(Uoptions.serviceVersionMajor)) {
86+
throw new IllegalArgumentException(
87+
"The provided descriptor does not contain the required uProtocol options.");
88+
}
6989

7090
UUri.Builder builder = UUri.newBuilder()
7191
.setUeId(options.<Integer>getExtension(Uoptions.serviceId))
7292
.setUeVersionMajor(options.<Integer>getExtension(Uoptions.serviceVersionMajor))
7393
.setResourceId(resourceId);
7494

75-
if (authorityName != null && !authorityName.isEmpty()) {
76-
builder.setAuthorityName(authorityName);
77-
}
78-
return builder.build();
95+
Optional.ofNullable(authorityName).ifPresent(builder::setAuthorityName);
96+
final var uuri = builder.build();
97+
UriValidator.validate(uuri);
98+
return uuri;
7999
}
80100
}

src/main/java/org/eclipse/uprotocol/uri/serializer/UriSerializer.java

Lines changed: 96 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,114 +12,140 @@
1212
*/
1313
package org.eclipse.uprotocol.uri.serializer;
1414

15-
import org.eclipse.uprotocol.uri.factory.UriFactory;
15+
import java.net.URI;
16+
import java.util.Objects;
17+
import java.util.Optional;
18+
1619
import org.eclipse.uprotocol.uri.validator.UriValidator;
1720
import org.eclipse.uprotocol.v1.UUri;
1821

1922
/**
20-
* UUri Serializer that serializes a UUri to a long format string per
21-
* https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc.
23+
* Provides functionality for serializing and deserializing {@link UUri}s to/from their
24+
* corresponding URI representation as defined by the uProtocol specification.
25+
*
26+
* @see <a href="https://github.com/eclipse-uprotocol/uprotocol-spec/blob/v1.6.0-alpha.4/basics/uri.adoc">
27+
* uProtocol URI Specification</a>
2228
*/
2329
public interface UriSerializer {
2430

25-
31+
String SCHEME_UP = "up";
2632

2733
/**
28-
* Support for serializing {@link UUri} objects into their String format.
34+
* Serializes a {@link UUri} into its URI representation.
2935
*
30-
* @param uri {@link UUri} object to be serialized to the String format.
31-
* @return Returns the String format of the supplied {@link UUri} that can be
32-
* used as a sink or a source in a uProtocol publish communication.
36+
* @param uuri The UUri to be serialized.
37+
* @return The URI.
38+
* @throws NullPointerException if the UUri is null.
39+
* @throws IllegalArgumentException if the UUri does not comply with the UUri specification.
3340
*/
34-
static String serialize(UUri uri) {
35-
if (uri == null || UriValidator.isEmpty(uri)) {
36-
return "";
37-
}
38-
41+
// [impl->dsn~uri-authority-mapping~1]
42+
// [impl->dsn~uri-path-mapping~1]
43+
// [impl->req~uri-serialization~1]
44+
static String serialize(UUri uuri) {
45+
Objects.requireNonNull(uuri);
46+
UriValidator.validate(uuri);
3947
StringBuilder sb = new StringBuilder();
4048

41-
if (!uri.getAuthorityName().isBlank()) {
49+
if (!uuri.getAuthorityName().isBlank()) {
4250
sb.append("//");
43-
sb.append(uri.getAuthorityName());
51+
sb.append(uuri.getAuthorityName());
4452
}
4553

4654
sb.append("/");
4755
final var pathSegments = String.format("%X/%X/%X",
48-
uri.getUeId(),
49-
uri.getUeVersionMajor(),
50-
uri.getResourceId());
56+
uuri.getUeId(),
57+
uuri.getUeVersionMajor(),
58+
uuri.getResourceId());
5159
sb.append(pathSegments);
5260
return sb.toString();
5361
}
5462

5563
/**
56-
* Deserialize a String into a UUri object.
64+
* Deserializes a URI into a UUri.
5765
*
58-
* @param uProtocolUri A long format uProtocol URI.
59-
* @return Returns an UUri data object.
66+
* @param uProtocolUri The URI to deserialize.
67+
* @return The UUri.
68+
* @throws NullPointerException if the URI is null.
69+
* @throws IllegalArgumentException if the URI is invalid.
6070
*/
71+
// [impl->dsn~uri-authority-name-length~1]
72+
// [impl->dsn~uri-scheme~1]
73+
// [impl->dsn~uri-host-only~2]
74+
// [impl->dsn~uri-authority-mapping~1]
75+
// [impl->dsn~uri-path-mapping~1]
76+
// [impl->req~uri-serialization~1]
6177
static UUri deserialize(String uProtocolUri) {
62-
if (uProtocolUri == null) {
63-
return UUri.getDefaultInstance();
64-
}
78+
Objects.requireNonNull(uProtocolUri);
79+
final var parsedUri = URI.create(uProtocolUri);
80+
return deserialize(parsedUri);
81+
}
6582

66-
String uri = uProtocolUri.contains(":") ? uProtocolUri.substring(uProtocolUri.indexOf(":") + 1)
67-
: uProtocolUri
68-
.replace('\\', '/');
83+
/**
84+
* Deserializes a URI into a UUri.
85+
*
86+
* @param uProtocolUri The URI to deserialize.
87+
* @return The UUri.
88+
* @throws NullPointerException if the URI is null.
89+
* @throws IllegalArgumentException if the URI is invalid.
90+
*/
91+
// [impl->dsn~uri-authority-name-length~1]
92+
// [impl->dsn~uri-scheme~1]
93+
// [impl->dsn~uri-host-only~2]
94+
// [impl->dsn~uri-authority-mapping~1]
95+
// [impl->dsn~uri-path-mapping~1]
96+
// [impl->req~uri-serialization~1]
97+
static UUri deserialize(URI uProtocolUri) {
98+
Objects.requireNonNull(uProtocolUri);
99+
100+
if (uProtocolUri.getScheme() != null && !SCHEME_UP.equals(uProtocolUri.getScheme())) {
101+
throw new IllegalArgumentException("uProtocol URI must use '%s' scheme".formatted(SCHEME_UP));
102+
}
103+
if (uProtocolUri.getQuery() != null) {
104+
throw new IllegalArgumentException("uProtocol URI must not contain query");
105+
}
106+
if (uProtocolUri.getFragment() != null) {
107+
throw new IllegalArgumentException("uProtocol URI must not contain fragment");
108+
}
109+
UriValidator.validateParsedAuthority(uProtocolUri);
69110

70-
boolean isLocal = !uri.startsWith("//");
111+
final var pathSegments = uProtocolUri.getPath().split("/");
112+
if (pathSegments.length != 4) {
113+
throw new IllegalArgumentException("uProtocol URI must have exactly 3 path segments");
114+
}
71115

72-
final String[] uriParts = uri.split("/");
73-
final int numberOfPartsInUri = uriParts.length;
116+
final var builder = UUri.newBuilder();
117+
Optional.ofNullable(uProtocolUri.getAuthority()).ifPresent(builder::setAuthorityName);
74118

75-
if (numberOfPartsInUri == 0 || numberOfPartsInUri == 1) {
76-
return UUri.getDefaultInstance();
119+
if (pathSegments[1].isEmpty()) {
120+
throw new IllegalArgumentException("URI must contain non-empty entity ID");
77121
}
78-
79-
UUri.Builder builder = UUri.newBuilder();
80122
try {
81-
if (isLocal) {
82-
builder.setUeId(Integer.parseUnsignedInt(uriParts[1], 16));
83-
if (numberOfPartsInUri > 2) {
84-
builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[2], 16));
85-
86-
if (numberOfPartsInUri > 3) {
87-
builder.setResourceId(Integer.parseUnsignedInt(uriParts[3], 16));
88-
}
89-
}
90-
} else {
91-
// If authority is blank, it is an error
92-
if (uriParts[2].isBlank()) {
93-
return UUri.getDefaultInstance();
94-
}
95-
builder.setAuthorityName(uriParts[2]);
96-
97-
if (uriParts.length > 3) {
98-
builder.setUeId(Integer.parseUnsignedInt(uriParts[3], 16));
99-
if (numberOfPartsInUri > 4) {
100-
builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[4], 16));
101-
102-
if (numberOfPartsInUri > 5) {
103-
builder.setResourceId(Integer.parseUnsignedInt(uriParts[5], 16));
104-
}
105-
106-
}
107-
}
108-
}
123+
builder.setUeId(Integer.parseUnsignedInt(pathSegments[1], 16));
109124
} catch (NumberFormatException e) {
110-
return UUri.getDefaultInstance();
125+
throw new IllegalArgumentException("URI must contain 32 bit hex-encoded entity ID", e);
111126
}
112127

113-
// Ensure the major version is less than the wildcard
114-
if (builder.getUeVersionMajor() > UriFactory.WILDCARD_ENTITY_VERSION) {
115-
return UUri.getDefaultInstance();
128+
if (pathSegments[2].isEmpty()) {
129+
throw new IllegalArgumentException("URI must contain non-empty entity version");
116130
}
117-
118-
// Ensure the resource id is less than the wildcard
119-
if (builder.getResourceId() > UriFactory.WILDCARD_ENTITY_ID) {
120-
return UUri.getDefaultInstance();
131+
try {
132+
int versionMajor = Integer.parseUnsignedInt(pathSegments[2], 16);
133+
UriValidator.validateVersionMajor(versionMajor);
134+
builder.setUeVersionMajor(versionMajor);
135+
} catch (NumberFormatException e) {
136+
throw new IllegalArgumentException("URI must contain 8 bit hex-encoded entity version", e);
121137
}
122138

139+
// the fourth path segment can not be empty because the String.split() method excludes
140+
// trailing empty strings from the resulting array
141+
// it is therefore safe to simply parse it as an unsigned integer
142+
try {
143+
int resourceId = Integer.parseUnsignedInt(pathSegments[3], 16);
144+
UriValidator.validateResourceId(resourceId);
145+
builder.setResourceId(resourceId);
146+
} catch (NumberFormatException e) {
147+
throw new IllegalArgumentException("URI must contain 16 bit hex-encoded resource ID", e);
148+
}
123149
return builder.build();
124150
}
125151
}

0 commit comments

Comments
 (0)