diff --git a/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java b/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java index 9209c7123..e21bad4d6 100644 --- a/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java +++ b/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java @@ -21,6 +21,7 @@ import io.cloudevents.SpecVersion; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.impl.BaseCloudEventBuilder; +import io.cloudevents.core.validator.CloudEventValidator; import io.cloudevents.rw.CloudEventContextReader; import io.cloudevents.rw.CloudEventContextWriter; import io.cloudevents.rw.CloudEventRWException; @@ -119,7 +120,31 @@ public CloudEvent build() { throw createMissingAttributeException(TYPE); } - return new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions); + CloudEvent cloudEvent = new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions); + + String name = null; + try{ + // check if the header.validator.class is set as a system property, + if ((name = System.getProperty("header.validator.class")) != null){ + Class dynamicClass = Class.forName(name); + Object dynamicObject = dynamicClass.newInstance(); + + if (dynamicObject instanceof CloudEventValidator) { + // pluggable implementation of validation implementation + CloudEventValidator cloudEventValidator = (CloudEventValidator) dynamicObject; + + cloudEventValidator.validate(cloudEvent); + } + else { + throw new IllegalArgumentException("Passed class is not an instance of CloudEventValidator"); + } + } + } + catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException("Unable to load the header.validator.class passed as vm argument = " + name + ". Please check the classpath", e); + } + + return cloudEvent; } @Override diff --git a/core/src/main/java/io/cloudevents/core/validator/CloudEventValidator.java b/core/src/main/java/io/cloudevents/core/validator/CloudEventValidator.java new file mode 100644 index 000000000..f1b01a7fc --- /dev/null +++ b/core/src/main/java/io/cloudevents/core/validator/CloudEventValidator.java @@ -0,0 +1,16 @@ +package io.cloudevents.core.validator; + +import io.cloudevents.CloudEvent; + +/** + * Interface which defines validation for CloudEvents attributes and extensions. + */ +public interface CloudEventValidator { + + /** + * Validate the attributes of a CloudEvent. + * + * @param cloudEvent the CloudEvent to validate + */ + void validate(CloudEvent cloudEvent); +} diff --git a/core/src/test/java/io/cloudevents/core/v1/CloudEventBuilderTest.java b/core/src/test/java/io/cloudevents/core/v1/CloudEventBuilderTest.java index a0c6cc6ef..a3aaeb686 100644 --- a/core/src/test/java/io/cloudevents/core/v1/CloudEventBuilderTest.java +++ b/core/src/test/java/io/cloudevents/core/v1/CloudEventBuilderTest.java @@ -142,4 +142,48 @@ void testMissingType() { ).hasMessageContaining("Attribute 'type' cannot be null"); } + @Test + void testClassNotFoundExceptionForValidator(){ + + System.setProperty("header.validator.class", "io.cloudevents.core.v1.CustomCloudEventValidatorTest"); + assertThatCode(() -> CloudEventBuilder + .v1() + .withId("000") + .withSource(URI.create("http://localhost")) + .withType("aaa") + .withExtension("astring", 10) + .build() + ).hasMessageContaining("Unable to load the header.validator.class passed as vm argument"); + } + + @Test + void testClassCastExceptionForValidator(){ + + System.setProperty("header.validator.class", "io.cloudevents.core.v1.CustomCloudEventValidator"); + assertThatCode(() -> CloudEventBuilder + .v1() + .withId("000") + .withSource(URI.create("http://localhost")) + .withType("aaa") + .withExtension("astring", 10) + .build() + ).hasMessageContaining("Passed class is not an instance of CloudEventValidator"); + } + + /** + * This test is to check for the mandatory extension 'namespace' as per Organization need + */ + @Test + void testMissingNamespaceExtension(){ + + System.setProperty("header.validator.class", "io.cloudevents.core.v1.CustomCloudEventValidatorImpl"); + assertThatCode(() -> CloudEventBuilder + .v1() + .withId("000") + .withSource(URI.create("http://localhost")) + .withType("aaa") + .build() + ).hasMessageContaining("Extension 'namespace' cannot be null"); + } + } diff --git a/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidator.java b/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidator.java new file mode 100644 index 000000000..f0878ac6e --- /dev/null +++ b/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidator.java @@ -0,0 +1,11 @@ +package io.cloudevents.core.v1; + +/** + * Dummy validation class which does not implement CloudEventValidator class. + */ +public class CustomCloudEventValidator { + + void validate(){ + + } +} diff --git a/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidatorImpl.java b/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidatorImpl.java new file mode 100644 index 000000000..89c968bd4 --- /dev/null +++ b/core/src/test/java/io/cloudevents/core/v1/CustomCloudEventValidatorImpl.java @@ -0,0 +1,15 @@ +package io.cloudevents.core.v1; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.validator.CloudEventValidator; + +public class CustomCloudEventValidatorImpl implements CloudEventValidator { + @Override + public void validate(CloudEvent cloudEvent) { + + if (cloudEvent.getExtension("namespace") == null){ + throw new IllegalStateException("Extension 'namespace' cannot be null"); + } + + } +}