diff options
16 files changed, 835 insertions, 1850 deletions
diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/GroupValidationResult.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/GroupValidationResult.java index 283f36b9..6da36c19 100644 --- a/common-parameters/src/main/java/org/onap/policy/common/parameters/GroupValidationResult.java +++ b/common-parameters/src/main/java/org/onap/policy/common/parameters/GroupValidationResult.java @@ -1,20 +1,20 @@ -/*- +/* * ============LICENSE_START======================================================= * Copyright (C) 2018 Ericsson. All rights reserved. - * Modifications Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ @@ -66,7 +66,7 @@ public class GroupValidationResult implements ValidationResult { // Check if a validation result should be added for this declared field if (isIncludedField(field)) { // Set a validation result for the field - validationResultMap.put(field.getName(), getValidationResult(field, parameterGroup)); + validationResultMap.put(field.getName(), getSetValidationResult(field, parameterGroup)); } } @@ -75,13 +75,29 @@ public class GroupValidationResult implements ValidationResult { // Check if a validation result should be added for this declared field if (isIncludedField(field)) { // Set a validation result for the field - validationResultMap.putIfAbsent(field.getName(), getValidationResult(field, parameterGroup)); + validationResultMap.putIfAbsent(field.getName(), getSetValidationResult(field, parameterGroup)); } } } + + /** + * Construct a validation result for a field, updating "this" status. + * + * @param field The parameter field + * @param ParameterGroup The parameter group containing the field + * @return the validation result + * @throws Exception on accessing private fields + */ + private ValidationResult getSetValidationResult(Field field, ParameterGroup parameterGroup) { + ValidationResult result = getValidationResult(field, parameterGroup); + setResult(result.getStatus()); + + return result; + } + /** * Construct a validation result for a field. - * + * * @param field The parameter field * @param ParameterGroup The parameter group containing the field * @return the validation result @@ -92,6 +108,12 @@ public class GroupValidationResult implements ValidationResult { final Class<?> fieldType = field.getType(); final Object fieldObject = getObjectField(parameterGroup, field); + // perform null checks + ParameterValidationResult result = new ParameterValidationResult(field, fieldObject); + if (!result.isValid()) { + return result; + } + // Nested parameter groups are allowed if (ParameterGroup.class.isAssignableFrom(fieldType)) { return new GroupValidationResult((ParameterGroup) fieldObject); @@ -106,16 +128,16 @@ public class GroupValidationResult implements ValidationResult { // Collections of parameter groups are not allowed if (Collection.class.isAssignableFrom(field.getType())) { checkCollection4ParameterGroups(fieldName, fieldObject); - return new ParameterValidationResult(field, fieldObject); + return result; } // It's a regular parameter - return new ParameterValidationResult(field, fieldObject); + return result; } /** * Get the value of a field in an object using a getter found with reflection. - * + * * @param targetObject The object on which to read the field value * @param fieldName The name of the field * @return The field value @@ -156,7 +178,7 @@ public class GroupValidationResult implements ValidationResult { /** * Check if this field is a map of parameter groups indexed by string keys. - * + * * @param fieldName the name of the collection field. * @param mapObject the map object to check */ @@ -184,7 +206,7 @@ public class GroupValidationResult implements ValidationResult { /** * Check if this field contains parameter groups. - * + * * @param fieldName the name of the collection field. * @param collectionObject the collection object to check */ @@ -234,7 +256,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation result on a parameter group. - * + * * @param status The validation status the parameter group is receiving * @param message The validation message explaining the validation status */ @@ -247,7 +269,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation result on a parameter group. On a sequence of calls, the most serious validation status is * recorded, assuming the status enum ordinal increase in order of severity - * + * * @param status The validation status the parameter group is receiving */ public void setResult(final ValidationStatus status) { @@ -260,7 +282,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation result on a parameter in a parameter group. - * + * * @param parameterName The name of the parameter * @param status The validation status the field is receiving * @param message The validation message explaining the validation status @@ -281,7 +303,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation result on a nested parameter group. - * + * * @param parameterName The name of the parameter field * @param nestedValidationResult The validation result from a nested field */ @@ -304,7 +326,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation result on a nested parameter group map entry. - * + * * @param parameterName The name of the parameter field * @param key The key of the map entry * @param nestedMapValidationResult The validation result from a nested map entry @@ -329,7 +351,7 @@ public class GroupValidationResult implements ValidationResult { /** * Set the validation status on a group map entry. - * + * * @param parameterName The name of the parameter field * @param key The key of the map entry * @param status The validation status of the entry @@ -395,11 +417,11 @@ public class GroupValidationResult implements ValidationResult { return validationResultBuilder.toString(); } - + /** * Check if a field should be included for validation. - * + * * @param field the field to check for inclusion * @return true of the field should be included */ @@ -425,11 +447,11 @@ public class GroupValidationResult implements ValidationResult { superclassFields.add(field); } } - + // Check the next super class down currentClass = currentClass.getSuperclass(); } - + return superclassFields; } -}
\ No newline at end of file +} diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/ParameterValidationResult.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/ParameterValidationResult.java index 9c829f4b..e43c2d17 100644 --- a/common-parameters/src/main/java/org/onap/policy/common/parameters/ParameterValidationResult.java +++ b/common-parameters/src/main/java/org/onap/policy/common/parameters/ParameterValidationResult.java @@ -1,26 +1,31 @@ -/*- +/* * ============LICENSE_START======================================================= * Copyright (C) 2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ package org.onap.policy.common.parameters; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; /** * This class holds the result of the validation of a parameter. @@ -43,6 +48,47 @@ public class ParameterValidationResult implements ValidationResult { protected ParameterValidationResult(final Field field, final Object parameterValue) { this.field = field; this.parameterValue = parameterValue; + + if (parameterValue == null && getAnyAnnotation(field, NotNull.class) != null) { + setResult(ValidationStatus.INVALID, "is null"); + + } else if (parameterValue instanceof String && getAnyAnnotation(field, NotBlank.class) != null + && parameterValue.toString().trim().isEmpty()) { + setResult(ValidationStatus.INVALID, "must be a non-blank string"); + + } else if (parameterValue instanceof Number) { + Min minAnnot = field.getAnnotation(Min.class); + if (minAnnot != null && ((Number) parameterValue).longValue() < minAnnot.value()) { + setResult(ValidationStatus.INVALID, "must be >= " + minAnnot.value()); + } + } + } + + /** + * Gets an annotation for a field, first checking the field, itself, and then checking + * at the class level for the current and superclasses. + * + * @param field field of interest + * @param annotClass class of annotation that is desired + * @return the field's annotation, or {@code null} if it does not exist + */ + private static <T extends Annotation> T getAnyAnnotation(final Field field, Class<T> annotClass) { + T annot = field.getAnnotation(annotClass); + if (annot != null) { + return annot; + } + + // check class level + Class<?> clazz = field.getDeclaringClass(); + while (clazz != Object.class) { + if ((annot = clazz.getAnnotation(annotClass)) != null) { + return annot; + } + + clazz = clazz.getSuperclass(); + } + + return null; } /** @@ -66,14 +112,16 @@ public class ParameterValidationResult implements ValidationResult { } /** - * Set the validation result on on a parameter field. + * Set the validation result on on a parameter field. * @param status The validation status the field is receiving * @param message The validation message explaining the validation status */ @Override public void setResult(final ValidationStatus status, final String message) { - this.status = status; - this.message = message; + if (this.status == ValidationStatus.CLEAN) { + this.status = status; + this.message = message; + } } /** diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java new file mode 100644 index 00000000..9ad6d7e0 --- /dev/null +++ b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.parameters.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(FIELD) +public @interface Min { + + /** + * The minimum value allowed. + * + * @return the minimum value allowed + */ + long value(); +} diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java new file mode 100644 index 00000000..169fa593 --- /dev/null +++ b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java @@ -0,0 +1,37 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.parameters.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that a field (i.e., String) may not be empty. + */ +@Retention(RUNTIME) +@Target({TYPE, FIELD}) +public @interface NotBlank { + +} diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java new file mode 100644 index 00000000..3c7bc8b7 --- /dev/null +++ b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java @@ -0,0 +1,37 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.parameters.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that a field may not be null. + */ +@Retention(RUNTIME) +@Target({TYPE, FIELD}) +public @interface NotNull { + +} diff --git a/common-parameters/src/test/java/org/onap/policy/common/parameters/TestValidation.java b/common-parameters/src/test/java/org/onap/policy/common/parameters/TestValidation.java index fb08d325..57d69f8d 100644 --- a/common-parameters/src/test/java/org/onap/policy/common/parameters/TestValidation.java +++ b/common-parameters/src/test/java/org/onap/policy/common/parameters/TestValidation.java @@ -1,19 +1,20 @@ -/*- +/* * ============LICENSE_START======================================================= * Copyright (C) 2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ @@ -28,12 +29,43 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; - import org.junit.Test; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; import org.onap.policy.common.parameters.testclasses.TestParametersL00; import org.onap.policy.common.parameters.testclasses.TestParametersL10; public class TestValidation { + private static final String NOT_BLANK_STRING_MESSAGE = + "field 'notBlankString' type 'java.lang.String' value '' INVALID, must be a non-blank string\n" + .replace('\'', '"'); + + private static final String NULL_STRING_MESSAGE = + "field 'notNullString' type 'java.lang.String' value 'null' INVALID, is null\n".replace('\'', '"'); + + + private static final String NOT_BLANK_OBJECT_NAME = "notBlankObject"; + private static final String NOT_BLANK_STRING_NAME = "notBlankString"; + private static final String NOT_NULL_OBJECT_NAME = "notNullObject"; + private static final String NOT_NULL_STRING_NAME = "notNullString"; + private static final String MIN_LONG_NAME = "minLong"; + + @NotNull + private String notNullString; + + @NotNull + private Object notNullObject; + + @NotBlank + private String notBlankString; + + @NotBlank + private Object notBlankObject; + + @Min(value = 10) + private long minLong; + @Test public void testValidationOk() throws IOException { TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters"); @@ -50,11 +82,11 @@ public class TestValidation { .replaceAll("\\s+", ""); assertEquals(expectedResult, validationResult.getResult("", " ", true).replaceAll("\\s+", "")); } - + @Test public void testValidationObservation() throws IOException { TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters"); - + l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 3); String expectedResult = new String(Files.readAllBytes( @@ -65,7 +97,7 @@ public class TestValidation { assertTrue(validationResult.isValid()); assertFalse(validationResult.isClean()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 2); @@ -76,7 +108,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertTrue(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 1); @@ -87,7 +119,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertTrue(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 0); @@ -95,11 +127,11 @@ public class TestValidation { assertTrue(validationResult.isValid()); assertEquals(null, validationResult.getResult()); } - + @Test public void testValidationWarning() throws IOException { TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters"); - + l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 3); String expectedResult = new String(Files.readAllBytes( @@ -109,7 +141,7 @@ public class TestValidation { GroupValidationResult validationResult = l0Parameters.validate(); assertTrue(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 2); @@ -120,7 +152,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertTrue(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 1); @@ -131,7 +163,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertTrue(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 0); @@ -139,11 +171,11 @@ public class TestValidation { assertTrue(validationResult.isValid()); assertEquals(null, validationResult.getResult()); } - + @Test public void testValidationInvalid() throws IOException { TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters"); - + l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 3); String expectedResult = new String(Files.readAllBytes( @@ -153,7 +185,7 @@ public class TestValidation { GroupValidationResult validationResult = l0Parameters.validate(); assertFalse(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 2); @@ -164,7 +196,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertFalse(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 1); @@ -175,7 +207,7 @@ public class TestValidation { validationResult = l0Parameters.validate(); assertFalse(validationResult.isValid()); assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", "")); - + l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3); l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 0); @@ -183,7 +215,7 @@ public class TestValidation { assertTrue(validationResult.isValid()); assertEquals(null, validationResult.getResult()); } - + @Test public void testValidationEmptySubGroup() throws IOException { TestParametersL10 l10Parameters = new TestParametersL10("l10Parameters"); @@ -192,7 +224,201 @@ public class TestValidation { GroupValidationResult validationResult = l10Parameters.validate(); assertTrue(validationResult.isValid()); - + assertTrue(validationResult.getResult("", "", true).contains("UNDEFINED")); } + + @Test + public void testGetValidationResult() throws Exception { + Contained item = new Contained(); + item.setName("item"); + + Container cont = new Container(); + cont.item = item; + GroupValidationResult result = cont.validate(); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertTrue(result.getResult().contains(">= 1")); + + item.minInt = 1000; + result = cont.validate(); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + cont.item = null; + result = cont.validate(); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertTrue(result.getResult().contains("is null")); + } + + @Test + public void testParameterValidationResult_NotNull() throws Exception { + ParameterValidationResult result = new ParameterValidationResult( + TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), null); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals(NULL_STRING_MESSAGE, result.getResult()); + + // don't allow overwrite - values should remain unchanged + result.setResult(ValidationStatus.WARNING, "unknown"); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals(NULL_STRING_MESSAGE, result.getResult()); + + // non-null should be OK + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), ""); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + // non-null should be OK + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), "abc"); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + /* + * Check plain object fields, too. + */ + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_OBJECT_NAME), null); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals("field 'notNullObject' type 'java.lang.Object' value 'null' INVALID, is null\n".replace('\'', '"'), + result.getResult()); + + // non-null should be OK + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_OBJECT_NAME), + new Object()); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + /* + * Class-level annotation. + */ + + result = new ParameterValidationResult(NotNullSub.class.getDeclaredField(NOT_NULL_STRING_NAME), null); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals(NULL_STRING_MESSAGE, result.getResult()); + + // non-null should be OK + result = new ParameterValidationResult(NotNullSub.class.getDeclaredField(NOT_NULL_STRING_NAME), ""); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + } + + @Test + public void testParameterValidationResult_NotBlank() throws Exception { + ParameterValidationResult result = + new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), ""); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals(NOT_BLANK_STRING_MESSAGE, result.getResult()); + + // spaces only + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), " \t"); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + + // null should be OK + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), null); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + // null should be OK + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), "abc"); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + /* + * Check plain object fields, too. + */ + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_OBJECT_NAME), null); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_OBJECT_NAME), + new Object()); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + /* + * Class-level annotation. + */ + result = new ParameterValidationResult(NotBlankSub.class.getDeclaredField(NOT_BLANK_STRING_NAME), ""); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals(NOT_BLANK_STRING_MESSAGE, result.getResult()); + + // non-null should be OK + result = new ParameterValidationResult(NotBlankSub.class.getDeclaredField(NOT_BLANK_STRING_NAME), "abc"); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + } + + @Test + public void testParameterValidationResult_Min() throws Exception { + ParameterValidationResult result = + new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 9L); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals("field 'minLong' type 'long' value '9' INVALID, must be >= 10\n".replace('\'', '"'), + result.getResult()); + + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), -2); + assertEquals(ValidationStatus.INVALID, result.getStatus()); + assertEquals("field 'minLong' type 'long' value '-2' INVALID, must be >= 10\n".replace('\'', '"'), + result.getResult()); + + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 10L); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + + result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 11); + assertEquals(ValidationStatus.CLEAN, result.getStatus()); + } + + // these classes are used to test class-level annotations + + @NotNull + private static class NotNullBase { + + } + + private static class NotNullSub extends NotNullBase { + @SuppressWarnings("unused") + private String notNullString; + } + + private static class NotBlankBase { + + } + + @NotBlank + private static class NotBlankSub extends NotBlankBase { + @SuppressWarnings("unused") + private String notBlankString; + } + + private abstract static class SimpleGroup implements ParameterGroup { + private String name; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + } + + private static class Contained extends SimpleGroup { + @Min(value = 1) + private int minInt; + + @Override + public GroupValidationResult validate() { + return new GroupValidationResult(this); + } + + @SuppressWarnings("unused") + public int getMinInt() { + return minInt; + } + } + + private static class Container extends SimpleGroup { + @NotNull + private Contained item; + + @Override + public GroupValidationResult validate() { + return new GroupValidationResult(this); + } + + @SuppressWarnings("unused") + public Contained getItem() { + return item; + } + } } diff --git a/common-parameters/src/test/java/org/onap/policy/common/parameters/testclasses/TestParametersLGeneric.java b/common-parameters/src/test/java/org/onap/policy/common/parameters/testclasses/TestParametersLGeneric.java index 2d263fc7..1e5764c6 100644 --- a/common-parameters/src/test/java/org/onap/policy/common/parameters/testclasses/TestParametersLGeneric.java +++ b/common-parameters/src/test/java/org/onap/policy/common/parameters/testclasses/TestParametersLGeneric.java @@ -1,19 +1,20 @@ -/*- +/* * ============LICENSE_START======================================================= * Copyright (C) 2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ @@ -21,25 +22,28 @@ package org.onap.policy.common.parameters.testclasses; import org.onap.policy.common.parameters.GroupValidationResult; -import org.onap.policy.common.parameters.ParameterConstants; import org.onap.policy.common.parameters.ParameterGroup; import org.onap.policy.common.parameters.ValidationStatus; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; public class TestParametersLGeneric implements ParameterGroup { private String name; private int lgenericIntField = 0; + + @NotNull @NotBlank private String lgenericStringField = "Legal " + this.getClass().getCanonicalName(); - + /** * Default constructor. */ public TestParametersLGeneric() { // Default Constructor } - + /** * Create a test parameter group. - * + * * @param name the parameter group name */ public TestParametersLGeneric(final String name) { @@ -68,7 +72,7 @@ public class TestParametersLGeneric implements ParameterGroup { /** * Trigger a validation message. - * + * * @param level Number of levels to recurse before stopping */ public void triggerValidationStatus(final ValidationStatus triggerStatus, int level) { @@ -111,18 +115,12 @@ public class TestParametersLGeneric implements ParameterGroup { public GroupValidationResult validate() { GroupValidationResult validationResult = new GroupValidationResult(this); - if (lgenericStringField == null || lgenericStringField.trim().length() == 0) { - validationResult.setResult("lgenericStringField", ValidationStatus.INVALID, - "lgenericStringField must be a non-blank string"); - } else if (lgenericStringField.equals("lgenericStringField")) { + if ("lgenericStringField".equals(lgenericStringField)) { validationResult.setResult("lgenericStringField", ValidationStatus.WARNING, "using the field name for the parameter value is dangerous"); - } else if (lgenericStringField.equals("aString")) { + } else if ("aString".equals(lgenericStringField)) { validationResult.setResult("lgenericStringField", ValidationStatus.OBSERVATION, "this value for name is unhelpful"); - } else { - validationResult.setResult("lgenericStringField", ValidationStatus.CLEAN, - ParameterConstants.PARAMETER_HAS_STATUS_MESSAGE + ValidationStatus.CLEAN.toString()); } if (lgenericIntField < 0) { @@ -134,9 +132,6 @@ public class TestParametersLGeneric implements ParameterGroup { } else if (lgenericIntField == 2) { validationResult.setResult("lgenericIntField", ValidationStatus.OBSERVATION, "this field has been set to 2"); - } else { - validationResult.setResult("lgenericIntField", ValidationStatus.CLEAN, - ParameterConstants.PARAMETER_HAS_STATUS_MESSAGE + ValidationStatus.CLEAN.toString()); } return validationResult; diff --git a/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_2_Invalid.txt b/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_2_Invalid.txt index 0dec4a61..d9301997 100644 --- a/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_2_Invalid.txt +++ b/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_2_Invalid.txt @@ -6,11 +6,11 @@ parameter group "l0Parameters" type "org.onap.policy.common.parameters.testclass field "l10StringField" type "java.lang.String" value "" INVALID, l10StringField must be a non-blank string parameter group "l00LGenericNested" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group map "l00LGenericNestedMap" INVALID, parameter group has status INVALID parameter group "l00LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group "l00LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
\ No newline at end of file + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
\ No newline at end of file diff --git a/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_3_Invalid.txt b/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_3_Invalid.txt index 762ef46c..e45a7a6b 100644 --- a/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_3_Invalid.txt +++ b/common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_3_Invalid.txt @@ -6,24 +6,24 @@ parameter group "l0Parameters" type "org.onap.policy.common.parameters.testclass field "l10StringField" type "java.lang.String" value "" INVALID, l10StringField must be a non-blank string parameter group "l10LGenericNested0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group "l10LGenericNested1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group map "l10LGenericNestedMap" INVALID, parameter group has status INVALID parameter group "l10LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group "l10LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group "l00LGenericNested" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group map "l00LGenericNestedMap" INVALID, parameter group has status INVALID parameter group "l00LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string parameter group "l00LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer - field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
\ No newline at end of file + field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
\ No newline at end of file diff --git a/policy-endpoints/pom.xml b/policy-endpoints/pom.xml index 1d8ae53c..53e2186f 100644 --- a/policy-endpoints/pom.xml +++ b/policy-endpoints/pom.xml @@ -19,7 +19,8 @@ ============LICENSE_END========================================================= --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -205,6 +206,12 @@ <artifactId>openpojo</artifactId> </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> <build> diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClient.java new file mode 100644 index 00000000..3188b8d4 --- /dev/null +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClient.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP PAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.endpoints.event.comm.client; + + +import java.util.List; + +import lombok.Getter; + +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Client for sending messages to a Topic using TopicSink. + */ +public class TopicSinkClient { + private static final Logger logger = LoggerFactory.getLogger(TopicSinkClient.class); + + /** + * Coder used to encode messages being sent to the topic. + */ + private static final Coder CODER = new StandardCoder(); + + /** + * Topic to which messages are published. + */ + @Getter + private final String topic; + + /** + * Where messages are published. + */ + private final TopicSink sink; + + /** + * Constructs the object. + * + * @param topic topic to which messages should be published + * @throws TopicSinkClientException if the topic does not exist + */ + public TopicSinkClient(final String topic) throws TopicSinkClientException { + this.topic = topic; + + final List<TopicSink> lst = getTopicSinks(topic); + if (lst.isEmpty()) { + throw new TopicSinkClientException("no sinks for topic: " + topic); + } + + this.sink = lst.get(0); + } + + /** + * Sends a message to the topic, after encoding the message as json. + * + * @param message message to be encoded and sent + * @return {@code true} if the message was successfully sent/enqueued, {@code false} otherwise + */ + public boolean send(final Object message) { + try { + final String json = CODER.encode(message); + return sink.send(json); + + } catch (RuntimeException | CoderException e) { + logger.warn("send to {} failed because of {}", topic, e.getMessage(), e); + return false; + } + } + + // the remaining methods are wrappers that can be overridden by junit tests + + /** + * Gets the sinks for a given topic. + * + * @param topic the topic of interest + * @return the sinks for the topic + */ + protected List<TopicSink> getTopicSinks(final String topic) { + return TopicEndpoint.manager.getTopicSinks(topic); + } +} diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientException.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientException.java new file mode 100644 index 00000000..608393b3 --- /dev/null +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientException.java @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP PAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.endpoints.event.comm.client; + +/** + * Exception thrown by TopicSink client classes. + */ +public class TopicSinkClientException extends Exception { + private static final long serialVersionUID = 1L; + + public TopicSinkClientException() { + super(); + } + + public TopicSinkClientException(final String message) { + super(message); + } + + public TopicSinkClientException(final Throwable cause) { + super(cause); + } + + public TopicSinkClientException(final String message, final Throwable cause) { + super(message, cause); + } + + public TopicSinkClientException(final String message, final Throwable cause, final boolean enableSuppression, + final boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientExceptionTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientExceptionTest.java new file mode 100644 index 00000000..c0814703 --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientExceptionTest.java @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP PAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.endpoints.event.comm.client; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onap.policy.common.utils.test.ExceptionsTester; + +public class TopicSinkClientExceptionTest { + + @Test + public void test() { + assertEquals(5, new ExceptionsTester().test(TopicSinkClientException.class)); + } +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientTest.java new file mode 100644 index 00000000..725c0418 --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/event/comm/client/TopicSinkClientTest.java @@ -0,0 +1,150 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP PAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.endpoints.event.comm.client; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.mockito.internal.util.reflection.Whitebox; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicListener; +import org.onap.policy.common.endpoints.event.comm.TopicSink; + +public class TopicSinkClientTest { + private static final String SINK_FIELD_NAME = "sink"; + private static final String TOPIC = "my-topic"; + + private TopicSinkClient client; + private TopicSink sink; + private List<TopicSink> sinks; + + /** + * Creates mocks and an initial client object. + * + * @throws Exception if an error occurs + */ + @Before + public void setUp() throws Exception { + sink = mock(TopicSink.class); + when(sink.send(anyString())).thenReturn(true); + + sinks = Arrays.asList(sink, null); + + client = new TopicSinkClient2(TOPIC); + } + + @AfterClass + public static void tearDown() throws Exception { + // clear all topics after the tests + TopicEndpoint.manager.shutdown(); + } + + /** + * Uses a real NO-OP topic sink. + */ + @Test + public void testGetTopicSinks() throws Exception { + // clear all topics and then configure one topic + TopicEndpoint.manager.shutdown(); + + final Properties props = new Properties(); + props.setProperty("noop.sink.topics", TOPIC); + TopicEndpoint.manager.addTopicSinks(props); + + sink = TopicEndpoint.manager.getNoopTopicSink(TOPIC); + assertNotNull(sink); + + final AtomicReference<String> evref = new AtomicReference<>(null); + + sink.register(new TopicListener() { + @Override + public void onTopicEvent(final CommInfrastructure infra, final String topic, final String event) { + evref.set(event); + } + }); + + sink.start(); + + client = new TopicSinkClient(TOPIC); + client.send(100); + + assertEquals("100", evref.get()); + } + + @Test + public void testTopicSinkClient_testGetTopic() { + assertEquals(TOPIC, client.getTopic()); + assertSame(sink, Whitebox.getInternalState(client, SINK_FIELD_NAME)); + + // unknown topic -> should throw exception + sinks = new LinkedList<>(); + assertThatThrownBy(() -> new TopicSinkClient2(TOPIC)).isInstanceOf(TopicSinkClientException.class) + .hasMessage("no sinks for topic: my-topic"); + } + + @Test + public void testSend() throws Exception { + client.send(Arrays.asList("abc", "def")); + verify(sink).send("['abc','def']".replace('\'', '"')); + + // sink send fails + when(sink.send(anyString())).thenReturn(false); + assertFalse(client.send("ghi")); + + // sink send throws an exception + final RuntimeException ex = new RuntimeException("expected exception"); + when(sink.send(anyString())).thenThrow(ex); + assertFalse(client.send("jkl")); + } + + /** + * TopicSinkClient with some overrides. + */ + private class TopicSinkClient2 extends TopicSinkClient { + + public TopicSinkClient2(final String topic) throws TopicSinkClientException { + super(topic); + } + + @Override + protected List<TopicSink> getTopicSinks(final String topic) { + return sinks; + } + } +} diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java deleted file mode 100644 index b41f3bc0..00000000 --- a/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java +++ /dev/null @@ -1,498 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * ONAP Policy Engine - Common Modules - * ================================================================================ - * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.common.utils.properties; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Properties; -import org.apache.commons.lang3.StringUtils; -import org.onap.policy.common.utils.properties.exception.PropertyAccessException; -import org.onap.policy.common.utils.properties.exception.PropertyException; -import org.onap.policy.common.utils.properties.exception.PropertyInvalidException; -import org.onap.policy.common.utils.properties.exception.PropertyMissingException; - -/** - * Configuration whose fields are initialized by reading from a set of {@link Properties}, - * as directed by the {@link Property} annotations that appear on fields within the - * subclass. The values of the fields are set via <i>setXxx()</i> methods. As a result, if - * a field is annotated and there is no corresponding <i>setXxx()</i> method, then an - * exception will be thrown. - * - * <p>It is possible that an invalid <i>defaultValue</i> is specified via the - * {@link Property} annotation. This could remain undetected until an optional property is - * left out of the {@link Properties}. Consequently, this class will always validate a - * {@link Property}'s default value, if the <i>defaultValue</i> is not empty or if - * <i>accept</i> includes the "empty" option. - */ -public class PropertyConfiguration { - - /** - * The "empty" option that may appear within the {@link Property}'s <i>accept</i> - * attribute. - */ - public static final String ACCEPT_EMPTY = "empty"; - - /** - * Constructs a configuration, without populating any fields; fields should be - * populated later by invoking {@link #setAllFields(Properties)}. - */ - public PropertyConfiguration() { - super(); - } - - /** - * Initializes each "@Property" field with its value, as found in the properties. - * - * @param props properties from which to extract the values - * @throws PropertyException if an error occurs - */ - public PropertyConfiguration(Properties props) throws PropertyException { - setAllFields(props); - } - - /** - * Walks the class hierarchy of "this" object, populating fields defined in each - * class, using values extracted from the given property set. - * - * @param props properties from which to extract the values - * @throws PropertyException if an error occurs - */ - public void setAllFields(Properties props) throws PropertyException { - Class<?> clazz = getClass(); - - while (clazz != PropertyConfiguration.class) { - for (Field field : clazz.getDeclaredFields()) { - setValue(field, props); - } - - clazz = clazz.getSuperclass(); - } - } - - /** - * Sets a field's value, within an object, based on what's in the properties. - * - * @param field field whose value is to be set - * @param props properties from which to get the value - * @return {@code true} if the property's value was set, {@code false} otherwise - * @throws PropertyException if an error occurs - */ - protected boolean setValue(Field field, Properties props) throws PropertyException { - Property prop = field.getAnnotation(Property.class); - if (prop == null) { - return false; - } - - checkModifiable(field, prop); - - Method setter = getSetter(field, prop); - checkSetter(setter, prop); - - if (setValue(setter, field, props, prop)) { - return true; - } - - throw new PropertyAccessException(prop.name(), field.getName(), "unsupported field type"); - } - - /** - * Sets a field's value from a particular property. - * - * @param setter method to be used to set the field's value - * @param field field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return {@code true} if the property's value was set, {@code false} otherwise - * @throws PropertyException if an error occurs - */ - protected boolean setValue(Method setter, Field field, Properties props, Property prop) throws PropertyException { - - try { - Object val = getValue(field, props, prop); - if (val == null) { - return false; - - } else { - setter.invoke(this, val); - return true; - } - - } catch (IllegalArgumentException e) { - throw new PropertyInvalidException(prop.name(), field.getName(), e); - - } catch (IllegalAccessException | InvocationTargetException e) { - throw new PropertyAccessException(prop.name(), setter.getName(), e); - } - } - - /** - * Get the setter. - * - * @param field field whose value is to be set - * @param prop property of interest - * @return the method to be used to set the field's value - * @throws PropertyAccessException if a "set" method cannot be identified - */ - private Method getSetter(Field field, Property prop) throws PropertyAccessException { - String nm = "set" + StringUtils.capitalize(field.getName()); - - try { - return this.getClass().getMethod(nm, field.getType()); - - } catch (NoSuchMethodException | SecurityException e) { - throw new PropertyAccessException(prop.name(), nm, e); - } - } - - /** - * Gets a property value, coercing it to the field's type. - * - * @param field field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property, or {@code null} if the field type is - * not supported - * @throws PropertyException if an error occurs - */ - protected Object getValue(Field field, Properties props, Property prop) throws PropertyException { - - Class<?> clazz = field.getType(); - String fieldName = field.getName(); - - // can still add support for short, float, double, enum - - if (clazz == String.class) { - return getStringValue(fieldName, props, prop); - - } else if (clazz == Boolean.class || clazz == boolean.class) { - return getBooleanValue(fieldName, props, prop); - - } else if (clazz == Integer.class || clazz == int.class) { - return getIntegerValue(fieldName, props, prop); - - } else if (clazz == Long.class || clazz == long.class) { - return getLongValue(fieldName, props, prop); - - } else { - return null; - } - } - - /** - * Verifies that the field can be modified, i.e., it's neither <i>static</i>, nor - * <i>final</i>. - * - * @param field field whose value is to be set - * @param prop property of interest - * @throws PropertyAccessException if the field is not modifiable - */ - protected void checkModifiable(Field field, Property prop) throws PropertyAccessException { - int mod = field.getModifiers(); - - if (Modifier.isStatic(mod)) { - throw new PropertyAccessException(prop.name(), field.getName(), "'static' variable cannot be modified"); - } - - if (Modifier.isFinal(mod)) { - throw new PropertyAccessException(prop.name(), field.getName(), "'final' variable cannot be modified"); - } - } - - /** - * Verifies that the setter method is not <i>static</i>. - * - * @param setter method to be checked - * @param prop property of interest - * @throws PropertyAccessException if the method is static - */ - private void checkSetter(Method setter, Property prop) throws PropertyAccessException { - int mod = setter.getModifiers(); - - if (Modifier.isStatic(mod)) { - throw new PropertyAccessException(prop.name(), setter.getName(), "method is 'static'"); - } - } - - /** - * Gets a property value, coercing it to a String. - * - * @param fieldName field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property - * @throws PropertyException if an error occurs - */ - protected String getStringValue(String fieldName, Properties props, Property prop) throws PropertyException { - - /* - * Note: the default value for a String type is always valid, thus no need to - * check it. - */ - - return getPropValue(fieldName, props, prop); - } - - /** - * Gets a property value, coercing it to a Boolean. - * - * @param fieldName field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property - * @throws PropertyException if an error occurs - */ - protected Boolean getBooleanValue(String fieldName, Properties props, Property prop) throws PropertyException { - // validate the default value - checkDefaultValue(fieldName, prop, xxx -> makeBoolean(fieldName, prop, prop.defaultValue())); - - return makeBoolean(fieldName, prop, getPropValue(fieldName, props, prop)); - } - - /** - * Gets a property value, coercing it to an Integer. - * - * @param fieldName field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property - * @throws PropertyException if an error occurs - */ - protected Integer getIntegerValue(String fieldName, Properties props, Property prop) throws PropertyException { - // validate the default value - checkDefaultValue(fieldName, prop, xxx -> makeInteger(fieldName, prop, prop.defaultValue())); - - return makeInteger(fieldName, prop, getPropValue(fieldName, props, prop)); - } - - /** - * Gets a property value, coercing it to a Long. - * - * @param fieldName field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property - * @throws PropertyException if an error occurs - */ - protected Long getLongValue(String fieldName, Properties props, Property prop) throws PropertyException { - // validate the default value - checkDefaultValue(fieldName, prop, xxx -> makeLong(fieldName, prop, prop.defaultValue())); - - return makeLong(fieldName, prop, getPropValue(fieldName, props, prop)); - } - - /** - * Gets a value from the property set. - * - * @param fieldName field whose value is to be set - * @param props properties from which to get the value - * @param prop property of interest - * @return the value extracted from the property, or the <i>defaultValue</i> if the - * value does not exist - * @throws PropertyMissingException if the property does not exist and the - * <i>defaultValue</i> is empty and <i>emptyOk</i> is {@code false} - */ - protected String getPropValue(String fieldName, Properties props, Property prop) throws PropertyMissingException { - String propnm = prop.name(); - - String val = getRawPropertyValue(props, propnm); - if (val != null && isEmptyOk(prop, val)) { - return val; - } - - val = prop.defaultValue(); - if (val != null && isEmptyOk(prop, val)) { - return val; - } - - throw new PropertyMissingException(prop.name(), fieldName); - } - - /** - * Gets the property value, straight from the property set. - * - * @param props properties from which to get the value - * @param propnm name of the property of interest - * @return the raw property value - */ - protected String getRawPropertyValue(Properties props, String propnm) { - return props.getProperty(propnm); - } - - /** - * Coerces a String value into a Boolean. - * - * @param fieldName field whose value is to be set - * @param prop property of interest - * @param value value to be coerced - * @return the Boolean value represented by the String value - * @throws PropertyInvalidException if the value does not represent a valid Boolean - */ - private Boolean makeBoolean(String fieldName, Property prop, String value) throws PropertyInvalidException { - if ("true".equalsIgnoreCase(value)) { - return Boolean.TRUE; - - } else if ("false".equalsIgnoreCase(value)) { - return Boolean.FALSE; - - } else { - throw new PropertyInvalidException(prop.name(), fieldName, "expecting 'true' or 'false'"); - } - } - - /** - * Coerces a String value into an Integer. - * - * @param fieldName field whose value is to be set - * @param prop property of interest - * @param value value to be coerced - * @return the Integer value represented by the String value - * @throws PropertyInvalidException if the value does not represent a valid Integer - */ - private Integer makeInteger(String fieldName, Property prop, String value) throws PropertyInvalidException { - try { - return Integer.valueOf(value); - - } catch (NumberFormatException e) { - throw new PropertyInvalidException(prop.name(), fieldName, e); - } - } - - /** - * Coerces a String value into a Long. - * - * @param fieldName field whose value is to be set - * @param prop property of interest - * @param value value to be coerced - * @return the Long value represented by the String value - * @throws PropertyInvalidException if the value does not represent a valid Long - */ - private Long makeLong(String fieldName, Property prop, String value) throws PropertyInvalidException { - try { - return Long.valueOf(value); - - } catch (NumberFormatException e) { - throw new PropertyInvalidException(prop.name(), fieldName, e); - } - } - - /** - * Applies a function to check a property's default value. If the function throws an - * exception about an invalid property, then it's re-thrown as an exception about an - * invalid <i>defaultValue</i>. - * - * @param fieldName name of the field being checked - * @param prop property of interest - * @param func function to invoke to check the default value - */ - private void checkDefaultValue(String fieldName, Property prop, CheckDefaultValueFunction func) - throws PropertyInvalidException { - - if (isEmptyOk(prop, prop.defaultValue())) { - try { - func.apply(null); - - } catch (PropertyInvalidException ex) { - throw new PropertyInvalidException(ex.getPropertyName(), fieldName, "defaultValue is invalid", ex); - } - } - } - - /** - * Determines if a value is OK, even if it's empty. - * - * @param prop property specifying what's acceptable - * @param value value to be checked - * @return {@code true} if the value is not empty or empty is allowed, {@code false} - * otherwise - */ - protected boolean isEmptyOk(Property prop, String value) { - return !value.isEmpty() || isEmptyOk(prop); - } - - /** - * Determines if a {@link Property}'s <i>accept</i> attribute includes the "empty" - * option. - * - * @param prop property whose <i>accept</i> attribute is to be examined - * @return {@code true} if the <i>accept</i> attribute includes "empty" - */ - protected boolean isEmptyOk(Property prop) { - for (String option : prop.accept().split(",")) { - if (ACCEPT_EMPTY.equals(option)) { - return true; - } - } - - return false; - } - - /** - * Functions to check a default value. - */ - @FunctionalInterface - private static interface CheckDefaultValueFunction { - - /** - * Checks the default value. - * - * @param arg always {@code null} - * @throws PropertyInvalidException if an error occurs - */ - public void apply(Void arg) throws PropertyInvalidException; - } - - /** - * Annotation that declares a variable to be configured via {@link Properties}. - */ - @Target(ElementType.FIELD) - @Retention(RetentionPolicy.RUNTIME) - - protected static @interface Property { - - /** - * Name of the property. - * - * @return the property name - */ - public String name(); - - /** - * Default value, used when the property does not exist. - * - * @return the default value - */ - public String defaultValue() default ""; - - /** - * Comma-separated options identifying what's acceptable. The word, "empty", - * indicates that an empty string, "", is an acceptable value. - * - * @return options identifying what's acceptable - */ - public String accept() default ""; - } -} diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java deleted file mode 100644 index dbe04aee..00000000 --- a/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java +++ /dev/null @@ -1,1268 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * ONAP Policy Engine - Common Modules - * ================================================================================ - * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.common.utils.properties; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import java.lang.reflect.Field; -import java.util.Properties; -import org.junit.Before; -import org.junit.Test; -import org.onap.policy.common.utils.properties.exception.PropertyAccessException; -import org.onap.policy.common.utils.properties.exception.PropertyException; -import org.onap.policy.common.utils.properties.exception.PropertyInvalidException; -import org.onap.policy.common.utils.properties.exception.PropertyMissingException; - -/** - * Test class for PropertyConfiguration. - */ -public class PropertyConfigurationTest { - - /** - * Property used for most of the simple configuration subclasses. - */ - private static final String THE_VALUE = "the.value"; - - /** - * String property value. - */ - private static final String STRING_VALUE = "a string"; - - /** - * Default value for string property. - */ - private static final String STRING_VALUE_DEFAULT = "another string"; - - /** - * Value that cannot be coerced into any other type. - */ - private static final String INVALID_VALUE = "invalid"; - - /** - * Properties used when invoking constructors. - */ - private Properties props; - - @Before - public void setUp() { - props = new Properties(); - } - - @Test - public void testPropertyConfiguration() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - - PlainStringConfig cfg = new PlainStringConfig(); - assertEquals(null, cfg.value); - - cfg.setAllFields(props); - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testPropertyConfigurationProperties() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - PlainStringConfig cfg = new PlainStringConfig(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testSetAllFields() throws Exception { - - /* - * Implements an extra interface, just to see that it doesn't cause issues. - */ - class GrandParentConfig extends PropertyConfiguration implements DoesNothing { - - @Property(name = "grandparent.value") - protected boolean grandparentValue; - - @SuppressWarnings("unused") - public void setGrandparentValue(boolean grandparentValue) { - this.grandparentValue = grandparentValue; - } - } - - /* - * Implements the extra interface, too. - */ - class ParentConfig extends GrandParentConfig implements DoesNothing { - - @Property(name = "parent.value") - protected long parentValue; - - @SuppressWarnings("unused") - public void setParentValue(long parentValue) { - this.parentValue = parentValue; - } - } - - class Config extends ParentConfig { - - @Property(name = THE_VALUE) - private String value; - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - - final Config cfg = new Config(); - - // try one set of values - props.setProperty(THE_VALUE, STRING_VALUE); - props.setProperty("parent.value", "50000"); - props.setProperty("grandparent.value", "true"); - cfg.setAllFields(props); - - assertEquals(STRING_VALUE, cfg.value); - assertEquals(50000L, cfg.parentValue); - assertEquals(true, cfg.grandparentValue); - - // now a different set of values - props.setProperty(THE_VALUE, STRING_VALUE + "x"); - props.setProperty("parent.value", "50001"); - props.setProperty("grandparent.value", "false"); - cfg.setAllFields(props); - - assertEquals(STRING_VALUE + "x", cfg.value); - assertEquals(50001L, cfg.parentValue); - assertEquals(false, cfg.grandparentValue); - } - - @Test - public void testSetAllFields_NoProperties() throws Exception { - - class Config extends PropertyConfiguration { - - private String value; - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - - Config cfg = new Config(); - - props.setProperty(THE_VALUE, STRING_VALUE); - cfg.setAllFields(props); - - assertEquals(null, cfg.value); - } - - @Test - public void testSetValueFieldProperties_FieldSet() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - PlainStringConfig cfg = new PlainStringConfig(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testSetValueFieldProperties_NoAnnotation() throws PropertyException { - class Config extends PropertyConfiguration { - - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); - - assertNull(cfg.value); - } - - @Test(expected = PropertyAccessException.class) - public void testSetValueFieldProperties_WrongFieldType() throws PropertyException { - class Config extends PropertyConfiguration { - - // Cannot set a property into an "Exception" field - @Property(name = THE_VALUE) - private Exception value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Exception value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - new Config(props); - } - - @Test(expected = PropertyAccessException.class) - public void testGetSetter_NoSetter() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - new Config(props); - } - - @Test(expected = PropertyMissingException.class) - public void testSetValueMethodFieldPropertiesProperty_NoProperty_NoDefault() throws PropertyException { - new PlainStringConfig(props); - } - - @Test(expected = PropertyInvalidException.class) - public void testSetValueMethodFieldPropertiesProperty_InvalidValue() throws PropertyException { - class Config extends PlainPrimIntConfig { - - public Config(Properties props) throws PropertyException { - super(props); - } - - /** - * This returns a boolean, but the field is an "int", so it should throw an - * exception when it tries to stuff the value into the field. - */ - @Override - protected Object getValue(Field field, Properties props, Property prop) throws PropertyException { - return Boolean.TRUE; - } - } - - new Config(props); - } - - @Test(expected = PropertyAccessException.class) - public void testSetValueMethodFieldPropertiesProperty_MethodEx() throws PropertyException { - class Config extends PlainStringConfig { - - public Config(Properties props) throws PropertyException { - super(props); - } - - @Override - public void setValue(String value) { - throw new IllegalArgumentException("expected exception"); - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - new Config(props); - } - - @Test - public void testGetValue() throws PropertyException { - // this class contains all of the supported field types - class Config extends PropertyConfiguration { - - @Property(name = "string") - private String stringValue; - - @Property(name = "boolean.true") - private Boolean boolTrueValue; - - @Property(name = "boolean.false") - private Boolean boolFalseValue; - - @Property(name = "primitive.boolean.true") - private boolean primBoolTrueValue; - - @Property(name = "primitive.boolean.false") - private boolean primBoolFalseValue; - - @Property(name = "integer") - private Integer intValue; - - @Property(name = "primitive.integer") - private int primIntValue; - - @Property(name = "long") - private Long longValue; - - @Property(name = "primitive.long") - private long primLongValue; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public String getStringValue() { - return stringValue; - } - - @SuppressWarnings("unused") - public void setStringValue(String stringValue) { - this.stringValue = stringValue; - } - - @SuppressWarnings("unused") - public Boolean getBoolTrueValue() { - return boolTrueValue; - } - - @SuppressWarnings("unused") - public void setBoolTrueValue(Boolean boolTrueValue) { - this.boolTrueValue = boolTrueValue; - } - - @SuppressWarnings("unused") - public Boolean getBoolFalseValue() { - return boolFalseValue; - } - - @SuppressWarnings("unused") - public void setBoolFalseValue(Boolean boolFalseValue) { - this.boolFalseValue = boolFalseValue; - } - - @SuppressWarnings("unused") - public boolean isPrimBoolTrueValue() { - return primBoolTrueValue; - } - - @SuppressWarnings("unused") - public void setPrimBoolTrueValue(boolean primBoolTrueValue) { - this.primBoolTrueValue = primBoolTrueValue; - } - - @SuppressWarnings("unused") - public boolean isPrimBoolFalseValue() { - return primBoolFalseValue; - } - - @SuppressWarnings("unused") - public void setPrimBoolFalseValue(boolean primBoolFalseValue) { - this.primBoolFalseValue = primBoolFalseValue; - } - - @SuppressWarnings("unused") - public Integer getIntValue() { - return intValue; - } - - @SuppressWarnings("unused") - public void setIntValue(Integer intValue) { - this.intValue = intValue; - } - - @SuppressWarnings("unused") - public int getPrimIntValue() { - return primIntValue; - } - - @SuppressWarnings("unused") - public void setPrimIntValue(int primIntValue) { - this.primIntValue = primIntValue; - } - - @SuppressWarnings("unused") - public Long getLongValue() { - return longValue; - } - - @SuppressWarnings("unused") - public void setLongValue(Long longValue) { - this.longValue = longValue; - } - - @SuppressWarnings("unused") - public long getPrimLongValue() { - return primLongValue; - } - - @SuppressWarnings("unused") - public void setPrimLongValue(long primLongValue) { - this.primLongValue = primLongValue; - } - } - - props.setProperty("string", "a string"); - props.setProperty("boolean.true", "true"); - props.setProperty("boolean.false", "false"); - props.setProperty("primitive.boolean.true", "true"); - props.setProperty("primitive.boolean.false", "false"); - props.setProperty("integer", "100"); - props.setProperty("primitive.integer", "101"); - props.setProperty("long", "10000"); - props.setProperty("primitive.long", "10001"); - - Config cfg = new Config(props); - - assertEquals("a string", cfg.stringValue); - assertEquals(true, cfg.boolTrueValue); - assertEquals(false, cfg.boolFalseValue); - assertEquals(true, cfg.primBoolTrueValue); - assertEquals(false, cfg.primBoolFalseValue); - assertEquals(100, cfg.intValue.intValue()); - assertEquals(101, cfg.primIntValue); - assertEquals(10000, cfg.longValue.longValue()); - assertEquals(10001, cfg.primLongValue); - } - - @Test(expected = PropertyAccessException.class) - public void testGetValue_UnsupportedType() throws PropertyException { - class Config extends PropertyConfiguration { - - // Cannot set a property into an "Exception" field - @Property(name = THE_VALUE) - private Exception value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Exception value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - new Config(props); - } - - @Test - public void testCheckModifiable_OtherModifiers() throws PropertyException { - // this class contains all of the supported field types - class Config extends PropertyConfiguration { - - @Property(name = "public") - public String publicString; - - @Property(name = "private") - private String privateString; - - @Property(name = "protected") - protected String protectedString; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setPublicString(String publicString) { - this.publicString = publicString; - } - - @SuppressWarnings("unused") - public void setPrivateString(String privateString) { - this.privateString = privateString; - } - - @SuppressWarnings("unused") - public void setProtectedString(String protectedString) { - this.protectedString = protectedString; - } - } - - props.setProperty("public", "a public string"); - props.setProperty("private", "a private string"); - props.setProperty("protected", "a protected string"); - - Config cfg = new Config(props); - - assertEquals("a public string", cfg.publicString); - assertEquals("a private string", cfg.privateString); - assertEquals("a protected string", cfg.protectedString); - } - - @Test(expected = PropertyAccessException.class) - public void testCheckModifiable_Static() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - new StaticPropConfig(props); - } - - @Test(expected = PropertyAccessException.class) - public void testCheckModifiable_Final() throws PropertyException { - class Config extends PropertyConfiguration { - - // Cannot set a property into an "final" field - @Property(name = THE_VALUE) - private final String value = ""; - - public Config(Properties props) throws PropertyException { - super(props); - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - new Config(props); - } - - @Test(expected = PropertyAccessException.class) - public void testCheckSetter_Static() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - new StaticMethodConfig(props); - } - - @Test - public void testGetStringValue() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - PlainStringConfig cfg = new PlainStringConfig(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testGetBooleanValue_NoDefault() throws PropertyException { - props.setProperty(THE_VALUE, "true"); - PlainBooleanConfig cfg = new PlainBooleanConfig(props); - - assertEquals(true, cfg.value); - } - - @Test(expected = PropertyInvalidException.class) - public void testGetBooleanValue_InvalidDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = INVALID_VALUE) - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Boolean value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, "true"); - new Config(props); - } - - @Test - public void testGetBooleanValue_ValidDefault_True() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "true") - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Boolean value) { - this.value = value; - } - } - - // property not defined - Config cfg = new Config(props); - assertEquals(true, cfg.value); - - // try again, with the property defined as true - props.setProperty(THE_VALUE, "true"); - cfg = new Config(props); - assertEquals(true, cfg.value); - - // try again, with the property defined as false - props.setProperty(THE_VALUE, "false"); - cfg = new Config(props); - assertEquals(false, cfg.value); - } - - @Test - public void testGetBooleanValue_ValidDefault_False() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "false") - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Boolean value) { - this.value = value; - } - } - - // property not defined - Config cfg = new Config(props); - assertEquals(false, cfg.value); - - // try again, with the property defined as true - props.setProperty(THE_VALUE, "true"); - cfg = new Config(props); - assertEquals(true, cfg.value); - - // try again, with the property defined as false - props.setProperty(THE_VALUE, "false"); - cfg = new Config(props); - assertEquals(false, cfg.value); - } - - @Test - public void testGetIntegerValue_NoDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Integer value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Integer value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, "200"); - Config cfg = new Config(props); - - assertEquals(200, cfg.value.intValue()); - } - - @Test(expected = PropertyInvalidException.class) - public void testGetIntegerValue_InvalidDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = INVALID_VALUE) - private Integer value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Integer value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, "200"); - new Config(props); - } - - @Test - public void testGetIntegerValue_ValidDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "201") - private Integer value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Integer value) { - this.value = value; - } - } - - // property not defined - Config cfg = new Config(props); - assertEquals(201, cfg.value.intValue()); - - // try again, with the property defined - props.setProperty(THE_VALUE, "200"); - cfg = new Config(props); - assertEquals(200, cfg.value.intValue()); - } - - @Test - public void testGetLongValue_NoDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Long value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, "20000"); - Config cfg = new Config(props); - - assertEquals(20000L, cfg.value.longValue()); - } - - @Test(expected = PropertyInvalidException.class) - public void testGetLongValue_InvalidDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = INVALID_VALUE) - private Long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Long value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, "20000"); - new Config(props); - } - - @Test - public void testGetLongValue_ValidDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "20001") - private Long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(Long value) { - this.value = value; - } - } - - // property not defined - Config cfg = new Config(props); - assertEquals(20001L, cfg.value.longValue()); - - // try again, with the property defined - props.setProperty(THE_VALUE, "20000"); - cfg = new Config(props); - assertEquals(20000L, cfg.value.longValue()); - } - - @Test - public void testGetPropValue_Prop_NoDefault() throws PropertyException { - props.setProperty(THE_VALUE, STRING_VALUE); - PlainStringConfig cfg = new PlainStringConfig(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testGetPropValue_Prop_Default() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = STRING_VALUE_DEFAULT) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testGetPropValue_EmptyProp_EmptyOk() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, accept = "empty") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - props.setProperty(THE_VALUE, ""); - Config cfg = new Config(props); - - assertEquals("", cfg.value); - } - - @Test - public void testGetPropValue_EmptyDefault_EmptyOk() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "empty") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - Config cfg = new Config(props); - - assertEquals("", cfg.value); - } - - @Test - public void testGetPropValue_Default_EmptyOk() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = STRING_VALUE, accept = "empty") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - Config cfg = new Config(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test(expected = PropertyMissingException.class) - public void testGetPropValue_EmptyDefault_EmptyNotOk() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - new Config(props); - } - - @Test - public void testGetPropValue_Default_EmptyNotOk() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = STRING_VALUE, accept = "") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - Config cfg = new Config(props); - - assertEquals(STRING_VALUE, cfg.value); - } - - @Test - public void testGetRawPropertyValue() throws PropertyException { - class Config extends PlainStringConfig { - - public Config(Properties props) throws PropertyException { - super(props); - } - - @Override - protected String getRawPropertyValue(Properties props, String propnm) { - return STRING_VALUE; - } - } - - Config cfg = new Config(props); - - assertEquals(STRING_VALUE, cfg.getValue()); - - } - - @Test - public void testMakeBoolean_True() throws PropertyException { - props.setProperty(THE_VALUE, "true"); - PlainBooleanConfig cfg = new PlainBooleanConfig(props); - - assertEquals(true, cfg.value); - } - - @Test - public void testMakeBoolean_False() throws PropertyException { - props.setProperty(THE_VALUE, "false"); - PlainBooleanConfig cfg = new PlainBooleanConfig(props); - - assertEquals(false, cfg.value); - } - - @Test(expected = PropertyInvalidException.class) - public void testMakeBoolean_Invalid() throws PropertyException { - props.setProperty(THE_VALUE, INVALID_VALUE); - new PlainBooleanConfig(props); - } - - @Test - public void testMakeInteger_Valid() throws PropertyException { - props.setProperty(THE_VALUE, "300"); - PlainPrimIntConfig cfg = new PlainPrimIntConfig(props); - - assertEquals(300, cfg.value); - } - - @Test(expected = PropertyInvalidException.class) - public void testMakeInteger_Invalid() throws PropertyException { - props.setProperty(THE_VALUE, INVALID_VALUE); - new PlainPrimIntConfig(props); - } - - @Test(expected = PropertyInvalidException.class) - public void testMakeInteger_TooBig() throws PropertyException { - props.setProperty(THE_VALUE, String.valueOf(Integer.MAX_VALUE + 10L)); - new PlainPrimIntConfig(props); - } - - @Test - public void testMakeLong_Valid() throws PropertyException { - props.setProperty(THE_VALUE, "30000"); - PlainPrimLongConfig cfg = new PlainPrimLongConfig(props); - - assertEquals(30000L, cfg.value); - } - - @Test(expected = PropertyInvalidException.class) - public void testMakeLong_Invalid() throws PropertyException { - props.setProperty(THE_VALUE, INVALID_VALUE); - new PlainPrimLongConfig(props); - } - - @Test - public void testCheckDefaultValue_NotEmpty_Valid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "700") - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(long value) { - this.value = value; - } - } - - Config cfg = new Config(props); - - assertEquals(700L, cfg.value); - } - - @Test(expected = PropertyInvalidException.class) - public void testCheckDefaultValue_NotEmpty_Invalid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = INVALID_VALUE) - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(long value) { - this.value = value; - } - } - - new Config(props); - } - - @Test(expected = PropertyInvalidException.class) - public void testCheckDefaultValue_Empty_EmptyOk_Invalid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "empty") - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(long value) { - this.value = value; - } - } - - new Config(props); - } - - @Test - public void testIsEmptyOkPropertyString_True() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "empty") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - // missing property - should default to "" - Config cfg = new Config(props); - assertEquals("", cfg.value); - - // add an empty property - should take the property's value - props.setProperty(THE_VALUE, ""); - cfg.setAllFields(props); - assertEquals("", cfg.value); - - // add the property - should take the property's value - props.setProperty(THE_VALUE, STRING_VALUE); - cfg.setAllFields(props); - assertEquals(STRING_VALUE, cfg.value); - } - - @Test(expected = PropertyMissingException.class) - public void testIsEmptyOkPropertyString_False() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "") - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(long value) { - this.value = value; - } - } - - new Config(props); - } - - @Test - public void testIsEmptyOkProperty_True() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "empty") - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(String value) { - this.value = value; - } - } - - Config cfg = new Config(props); - - assertEquals("", cfg.value); - } - - @Test(expected = PropertyMissingException.class) - public void testIsEmptyOkProperty_False() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE, defaultValue = "", accept = "") - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - - @SuppressWarnings("unused") - public void setValue(long value) { - this.value = value; - } - } - - new Config(props); - } - - /** - * Config with a String value having no qualifiers. - */ - public class PlainStringConfig extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public PlainStringConfig() { - - } - - public PlainStringConfig(Properties props) throws PropertyException { - super(props); - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - } - - /** - * Config with a Boolean value having no qualifiers. - */ - public class PlainBooleanConfig extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Boolean value; - - public PlainBooleanConfig(Properties props) throws PropertyException { - super(props); - } - - public void setValue(Boolean value) { - this.value = value; - } - } - - /** - * Config with an int value having no qualifiers. - */ - public class PlainPrimIntConfig extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private int value; - - public PlainPrimIntConfig(Properties props) throws PropertyException { - super(props); - } - - public void setValue(int value) { - this.value = value; - } - } - - /** - * Config with a long value having no qualifiers. - */ - public class PlainPrimLongConfig extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private long value; - - public PlainPrimLongConfig(Properties props) throws PropertyException { - super(props); - } - - public void setValue(long value) { - this.value = value; - } - } - - /** - * A config whose field is "static". - */ - public static class StaticPropConfig extends PropertyConfiguration { - - // "static" field cannot be set - @Property(name = THE_VALUE) - private static String value; - - public StaticPropConfig(Properties props) throws PropertyException { - super(props); - } - - public static void setValue(String value) { - StaticPropConfig.value = value; - } - } - - /** - * A config whose method is "static". - */ - public static class StaticMethodConfig extends PropertyConfiguration { - - // "static" field cannot be set - @Property(name = THE_VALUE) - private String value; - - public StaticMethodConfig(Properties props) throws PropertyException { - super(props); - } - - public static void setValue(String value) { - - } - } - - /** - * This is just used as a mix-in to ensure that the configuration ignores interfaces. - */ - public static interface DoesNothing { - - } -} |