aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Hahn <jrh3@att.com>2018-03-23 14:47:42 -0400
committerJim Hahn <jrh3@att.com>2018-03-25 09:03:26 -0400
commitd625f8e805c12301e36392d550b4ca8f8cfa3b5f (patch)
tree8b4cb9e2ef1c3d85e236f2a07a42427e880a8c99
parent586e553d141e087566a6b064121910089cf52cff (diff)
Add PropertyConfiguration class
Added PropertyConfiguration class, which provides for the loading of properties into the fields of a subclass. Annotations are used to identify the field to be loaded and the name of the property from which it is to be loaded. Changed attribute "useDefault" to "emptyOk" so that it only has to be specified when the default value is empty. Reformatted a couple of files. Added setAllFields() method. Added getRawPropertyValue() method so subclasses can change how values are extracted from the property set. Added SpecPropertyConfiguration so controller or session names may be embedded within property names. Fixed some typos. Refactored out generalizeType2(). Changed exception classes to take field name instead of Field object. Modified getValue() to take the field name and class type instead of Field object. Removed or fixed comments in junit tests. Added more junit tests. Changed emptyOk attribute to accept, to facilitate adding other options in the future. Corrected comments on the isEmptyOk() methods. Change-Id: I6b190fea687f87cf512fb3236bfcf40b7adc4566 Issue-ID: POLICY-577 Signed-off-by: Jim Hahn <jrh3@att.com>
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java465
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java233
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAccessException.java70
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationException.java69
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java142
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidException.java70
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyMissingException.java38
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java1031
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java331
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/BasicPropertyExceptionTester.java130
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAccessExceptionTest.java66
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationExceptionTest.java70
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyExceptionTest.java69
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidExceptionTest.java70
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyMissingExceptionTest.java42
15 files changed, 2896 insertions, 0 deletions
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
new file mode 100644
index 00000000..7253c746
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java
@@ -0,0 +1,465 @@
+/*
+ * ============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 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.Modifier;
+import java.util.Properties;
+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.
+ * <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);
+
+ if (setValue(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 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(Field field, Properties props, Property prop) throws PropertyException {
+
+ try {
+ Object val = getValue(field, props, prop);
+ if (val == null) {
+ return false;
+
+ } else {
+
+ /*
+ * According to java docs & blogs, "field" is our own copy, so we're free
+ * to change the flags without impacting the real permissions of the field
+ * within the real class.
+ */
+ field.setAccessible(true);
+
+ field.set(this, val);
+ return true;
+ }
+
+ } catch (IllegalArgumentException e) {
+ throw new PropertyInvalidException(prop.name(), field.getName(), e);
+
+ } catch (IllegalAccessException e) {
+ throw new PropertyAccessException(prop.name(), field.getName(), 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");
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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".equals(value.toLowerCase())) {
+ return Boolean.TRUE;
+
+ } else if ("false".equals(value.toLowerCase())) {
+ 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.getCause());
+ }
+ }
+ }
+
+ /**
+ * 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/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java
new file mode 100644
index 00000000..9e3767c1
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java
@@ -0,0 +1,233 @@
+/*
+ * ============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 java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.onap.policy.common.utils.properties.exception.PropertyException;
+
+/**
+ * PropertyConfiguration whose property names are specialized, using a specialization. A
+ * property name can take one of the following forms:
+ * <dl>
+ * <dt>aaa{$}Ddd</dt>
+ * <dd>if the specialization is "Xxx", then it looks for the value associated with the
+ * property named "aaaXxxDdd"</dd>
+ * <dt>aaa{Bbb?Ccc}Ddd</dt>
+ * <dd>if the specialization is "Xxx", then it looks for the value associated with the
+ * property named "aaaBbbXxxCccDdd". If the property does not exist, then it looks for the
+ * value associated with the property named "aaaDdd" (i.e., without the
+ * specialization)</dd>
+ * <dt>aaa</dt>
+ * <dd>simply looks for the value associated with the property named "aaa", without using
+ * the specialization</dd>
+ * </dl>
+ * <p>
+ * In the above examples, any of the components (e.g., "aaa") may be empty.
+ */
+public class SpecPropertyConfiguration extends PropertyConfiguration {
+
+ /**
+ * Pattern to extract the specializer from a property name. Group 1 matches the form,
+ * "{$}", while groups 2 and 3 match the prefix and suffix, respectively, of the form,
+ * "{prefix?suffix}".
+ */
+ private static final Pattern SPEC_PAT = Pattern.compile(""
+ // start of specialization info
+ + "\\{(?:"
+ // specialization type 1
+ + "(\\$)"
+ // alternative
+ + "|"
+ // specialization type 2
+ + "(?:"
+ // specialization type 2 prefix, may be empty
+ + "([^}?]*)"
+ // place-holder for the specialization, itself
+ + "\\?"
+ // specialization type 2 suffix, may be empty
+ + "([^}]*)"
+ // end of specialization type 2
+ + ")"
+ // end of specialization info
+ + ")\\}");
+
+ /**
+ * The specialization to be used within property names.
+ */
+ private final String specialization;
+
+ /**
+ * Constructs a configuration, without populating any fields; fields should be
+ * populated later by invoking {@link #setAllFields(Properties)}.
+ *
+ * @param specialization specialization to be substituted within property names
+ */
+ public SpecPropertyConfiguration(String specialization) {
+ super();
+
+ this.specialization = specialization;
+ }
+
+ /**
+ *
+ * Initializes each "@Property" field with its value, as found in the properties.
+ *
+ * @param specialization specialization to be substituted within property names
+ * @param props properties from which to extract the values
+ * @throws PropertyException if an error occurs
+ */
+ public SpecPropertyConfiguration(String specialization, Properties props) throws PropertyException {
+ super();
+
+ this.specialization = specialization;
+
+ setAllFields(props);
+ }
+
+ /**
+ * Gets a property's value, examining the property name for each of the types of
+ * specialization.
+ */
+ @Override
+ protected String getRawPropertyValue(Properties props, String propnm) {
+ Matcher mat = SPEC_PAT.matcher(propnm);
+
+ if (!mat.find()) {
+ // property name isn't specialized - use it as is
+ return super.getRawPropertyValue(props, propnm);
+
+ } else if (mat.group(1) != null) {
+ // replace "{$}" with the specialization name
+ return super.getRawPropertyValue(props, specializeType1(propnm, specialization, mat));
+
+ } else {
+ // first try to get the property using the specialization info
+ String val = super.getRawPropertyValue(props, specializeType2(propnm, specialization, mat));
+ if (val != null) {
+ return val;
+ }
+
+ // wasn't found - try again, without any specialization info
+ return super.getRawPropertyValue(props, generalizeType2(propnm, mat));
+ }
+ }
+
+ /**
+ * Generalizes a property name by stripping any specialization info from it. This is
+ * typically used to construct property names for junit testing.
+ *
+ * @param propnm property name to be stripped of specialization info
+ * @return the generalized property name
+ * @throws IllegalArgumentException if the property name requires specialization
+ * (i.e., contains "{$}")
+ */
+ public static String generalize(String propnm) {
+ Matcher mat = SPEC_PAT.matcher(propnm);
+
+ if (!mat.find()) {
+ // property name has no specialization info
+ return propnm;
+
+ } else if (mat.group(1) != null) {
+ // the "{$}" form requires specialization
+ throw new IllegalArgumentException("property requires specialization");
+
+ } else {
+ // property name has specialization info - strip it out
+ return generalizeType2(propnm, mat);
+ }
+ }
+
+ /**
+ *
+ * Generalizes a property name of specialization type 2 (i.e., "{xxx?yyy}" form).
+ *
+ * @param propnm property name to be stripped of specialization info
+ * @param matcher the matcher that matched the "{xxx?yyy}"
+ * @return the generalized property name
+ */
+ private static String generalizeType2(String propnm, Matcher mat) {
+ String prefix = propnm.substring(0, mat.start());
+ String suffix = propnm.substring(mat.end());
+
+ return prefix + suffix;
+ }
+
+ /**
+ * Specializes a property name by applying the specialization. This is typically used
+ * to construct property names for junit testing.
+ *
+ * @param propnm property name to be stripped of specialization info
+ * @param spec specialization to apply
+ * @return the specialized property name
+ */
+ public static String specialize(String propnm, String spec) {
+ Matcher mat = SPEC_PAT.matcher(propnm);
+
+ if (!mat.find()) {
+ // property name has no specialization info - leave it as is
+ return propnm;
+
+ } else if (mat.group(1) != null) {
+ // the "{$}" form requires specialization
+ return specializeType1(propnm, spec, mat);
+
+ } else {
+ // the "{xxx?yyy}" form requires specialization
+ return specializeType2(propnm, spec, mat);
+ }
+ }
+
+ /**
+ * Specializes a property name of specialization type 1 (i.e., "{$}" form).
+ *
+ * @param propnm property name to be stripped of specialization info
+ * @param spec specialization to apply
+ * @param matcher the matcher that matched the "{$}"
+ * @return the specialized property name
+ */
+ private static String specializeType1(String propnm, String spec, Matcher mat) {
+ String prefix = propnm.substring(0, mat.start());
+ String suffix = propnm.substring(mat.end());
+
+ return prefix + spec + suffix;
+ }
+
+ /**
+ * Specializes a property name of specialization type 2 (i.e., "{xxx?yyy}" form).
+ *
+ * @param propnm property name to be stripped of specialization info
+ * @param spec specialization to apply
+ * @param matcher the matcher that matched the "{xxx?yyy}"
+ * @return the specialized property name
+ */
+ private static String specializeType2(String propnm, String spec, Matcher matcher) {
+ String prefix = propnm.substring(0, matcher.start());
+ String suffix = propnm.substring(matcher.end());
+
+ String specPrefix = matcher.group(2);
+ String specSuffix = matcher.group(3);
+
+ return (prefix + specPrefix + spec + specSuffix + suffix);
+ }
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAccessException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAccessException.java
new file mode 100644
index 00000000..568a8a79
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAccessException.java
@@ -0,0 +1,70 @@
+/*
+ * ============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.exception;
+
+/**
+ * Exception indicating that a property's value cannot be converted to the type required
+ * by the target field.
+ */
+public class PropertyAccessException extends PropertyException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ */
+ public PropertyAccessException(String propnm, String fieldName) {
+ super(propnm, fieldName);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ */
+ public PropertyAccessException(String propnm, String fieldName, String message) {
+ super(propnm, fieldName, message);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param cause cause of the exception
+ */
+ public PropertyAccessException(String propnm, String fieldName, Throwable cause) {
+ super(propnm, fieldName, cause);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ * @param cause cause of the exception
+ */
+ public PropertyAccessException(String propnm, String fieldName, String message, Throwable cause) {
+ super(propnm, fieldName, message, cause);
+ }
+
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationException.java
new file mode 100644
index 00000000..2803ddfe
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationException.java
@@ -0,0 +1,69 @@
+/*
+ * ============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.exception;
+
+/**
+ * Exception indicating that a property annotation's definition (e.g., null name).
+ */
+public class PropertyAnnotationException extends PropertyException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ */
+ public PropertyAnnotationException(String propnm, String fieldName) {
+ super(propnm, fieldName);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ */
+ public PropertyAnnotationException(String propnm, String fieldName, String message) {
+ super(propnm, fieldName, message);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param cause cause of the exception
+ */
+ public PropertyAnnotationException(String propnm, String fieldName, Throwable cause) {
+ super(propnm, fieldName, cause);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ * @param cause cause of the exception
+ */
+ public PropertyAnnotationException(String propnm, String fieldName, String message, Throwable cause) {
+ super(propnm, fieldName, message, cause);
+ }
+
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java
new file mode 100644
index 00000000..8df4fb8e
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java
@@ -0,0 +1,142 @@
+/*
+ * ============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.exception;
+
+/**
+ * Exception associated with a Property.
+ */
+public class PropertyException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Name of the property for which the exception was thrown.
+ */
+ private final String propertyName;
+
+ /**
+ * Name of the field for which the exception was thrown.
+ */
+ private final String fieldName;
+
+ /**
+ *
+ * @param propName name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ */
+ public PropertyException(String propName, String fieldName) {
+ super(makeMessage(propName, fieldName));
+
+ this.propertyName = propName;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ */
+ public PropertyException(String propnm, String fieldName, String message) {
+ super(makeMessage(propnm, fieldName, message));
+
+ this.propertyName = propnm;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param cause cause of the exception
+ */
+ public PropertyException(String propnm, String fieldName, Throwable cause) {
+ super(makeMessage(propnm, fieldName), cause);
+
+ this.propertyName = propnm;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ * @param cause cause of the exception
+ */
+ public PropertyException(String propnm, String fieldName, String message, Throwable cause) {
+ super(makeMessage(propnm, fieldName, message), cause);
+
+ this.propertyName = propnm;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ *
+ * @return name of the property for which the exception was thrown, or {@code null} if
+ * no name was provided
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ *
+ * @return name of the field for which the exception was thrown, or {@code null} if no
+ * field was provided
+ */
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message, never {@code null}
+ * @return an error message composed of the three items
+ */
+ private static String makeMessage(String propnm, String fieldName, String message) {
+ return makeMessage(propnm, fieldName) + ": " + message;
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @return an error message composed of the two items
+ */
+ private static String makeMessage(String propnm, String fieldName) {
+ StringBuilder bldr = new StringBuilder(50);
+
+ if (propnm == null) {
+ bldr.append("property exception");
+
+ } else {
+ bldr.append("exception for property " + propnm);
+ }
+
+ if (fieldName != null) {
+ bldr.append(" with field " + fieldName);
+ }
+
+ return bldr.toString();
+ }
+
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidException.java
new file mode 100644
index 00000000..da1b6ae8
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidException.java
@@ -0,0 +1,70 @@
+/*
+ * ============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.exception;
+
+/**
+ * Exception indicating that a property's value cannot be converted to the type required
+ * by the target field.
+ */
+public class PropertyInvalidException extends PropertyException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ */
+ public PropertyInvalidException(String propnm, String fieldName) {
+ super(propnm, fieldName);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ */
+ public PropertyInvalidException(String propnm, String fieldName, String message) {
+ super(propnm, fieldName, message);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param cause cause of the exception
+ */
+ public PropertyInvalidException(String propnm, String fieldName, Throwable cause) {
+ super(propnm, fieldName, cause);
+ }
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ * @param message error message
+ * @param cause cause of the exception
+ */
+ public PropertyInvalidException(String propnm, String fieldName, String message, Throwable cause) {
+ super(propnm, fieldName, message, cause);
+ }
+
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyMissingException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyMissingException.java
new file mode 100644
index 00000000..de542e6a
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyMissingException.java
@@ -0,0 +1,38 @@
+/*
+ * ============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.exception;
+
+/**
+ * Exception indicating that a property is missing.
+ */
+public class PropertyMissingException extends PropertyException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * @param propnm name of the property causing the exception, or {@code null}
+ * @param fieldName name of the field causing the exception, or {@code null}
+ */
+ public PropertyMissingException(String propnm, String fieldName) {
+ super(propnm, fieldName);
+ }
+
+}
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
new file mode 100644
index 00000000..cf823b5d
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java
@@ -0,0 +1,1031 @@
+/*
+ * ============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;
+
+/**
+ *
+ */
+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 {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private String value;
+ };
+
+ props.setProperty(THE_VALUE, STRING_VALUE);
+
+ Config cfg = new Config();
+ assertEquals(null, cfg.value);
+
+ cfg.setAllFields(props);
+ assertEquals(STRING_VALUE, cfg.value);
+ }
+
+ @Test
+ public void testPropertyConfigurationProperties() 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);
+ Config cfg = new Config(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;
+ };
+
+ /*
+ * Implements the extra interface, too.
+ */
+ class ParentConfig extends GrandParentConfig implements DoesNothing {
+
+ @Property(name = "parent.value")
+ protected long parentValue;
+ };
+
+ class Config extends ParentConfig {
+
+ @Property(name = THE_VALUE)
+ private String value;
+ };
+
+
+ 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;
+ };
+
+
+ Config cfg = new Config();
+
+ props.setProperty(THE_VALUE, STRING_VALUE);
+ cfg.setAllFields(props);
+
+ assertEquals(null, cfg.value);
+ }
+
+ @Test
+ public void testSetValueFieldProperties_FieldSet() 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);
+ Config cfg = new Config(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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ props.setProperty(THE_VALUE, STRING_VALUE);
+ new Config(props);
+ }
+
+ @Test(expected = PropertyMissingException.class)
+ public void testSetValueFieldPropertyProperties_NoProperty_NoDefault() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private String value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ new Config(props);
+ }
+
+ @Test(expected = PropertyInvalidException.class)
+ public void testSetValueFieldPropertyProperties_InvalidValue() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private int value;
+
+ 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
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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 StaticConfig(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
+ public void testGetStringValue() 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);
+ Config cfg = new Config(props);
+
+ assertEquals(STRING_VALUE, cfg.value);
+ }
+
+ @Test
+ public void testGetBooleanValue_NoDefault() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private Boolean value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, "true");
+ Config cfg = new Config(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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ // 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);
+ }
+ };
+
+ // 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ // 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ // 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 {
+ 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);
+ Config cfg = new Config(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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ Config cfg = new Config(props);
+
+ assertEquals(STRING_VALUE, cfg.value);
+ }
+
+ @Test
+ public void testGetRawPropertyValue() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private String value;
+
+ 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.value);
+
+ }
+
+ @Test
+ public void testMakeBoolean_True() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private Boolean value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, "true");
+ Config cfg = new Config(props);
+
+ assertEquals(true, cfg.value);
+ }
+
+ @Test
+ public void testMakeBoolean_False() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private Boolean value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, "false");
+ Config cfg = new Config(props);
+
+ assertEquals(false, cfg.value);
+ }
+
+ @Test(expected = PropertyInvalidException.class)
+ public void testMakeBoolean_Invalid() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private Boolean value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, INVALID_VALUE);
+ new Config(props);
+ }
+
+ @Test
+ public void testMakeInteger_Valid() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private int value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, "300");
+ Config cfg = new Config(props);
+
+ assertEquals(300, cfg.value);
+ }
+
+ @Test(expected = PropertyInvalidException.class)
+ public void testMakeInteger_Invalid() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private int value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, INVALID_VALUE);
+ new Config(props);
+ }
+
+ @Test(expected = PropertyInvalidException.class)
+ public void testMakeInteger_TooBig() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private int value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, String.valueOf(Integer.MAX_VALUE + 10L));
+ new Config(props);
+ }
+
+ @Test
+ public void testMakeLong_Valid() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private long value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, "30000");
+ Config cfg = new Config(props);
+
+ assertEquals(30000L, cfg.value);
+ }
+
+ @Test(expected = PropertyInvalidException.class)
+ public void testMakeLong_Invalid() throws PropertyException {
+ class Config extends PropertyConfiguration {
+
+ @Property(name = THE_VALUE)
+ private long value;
+
+ public Config(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ props.setProperty(THE_VALUE, INVALID_VALUE);
+ new Config(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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ // 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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);
+ }
+ };
+
+ new Config(props);
+ }
+
+ /**
+ * A config whose annotated property is "static".
+ */
+ public static class StaticConfig extends PropertyConfiguration {
+
+ // "static" field cannot be set
+ @Property(name = THE_VALUE)
+ private static String value;
+
+ public StaticConfig(Properties props) throws PropertyException {
+ super(props);
+ }
+ };
+
+ /**
+ * This is just used as a mix-in to ensure that the configuration ignores interfaces.
+ */
+ public static interface DoesNothing {
+
+ }
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java
new file mode 100644
index 00000000..39c8f01a
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java
@@ -0,0 +1,331 @@
+/*
+ * ============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.*;
+import java.util.Properties;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.properties.exception.PropertyException;
+import org.onap.policy.common.utils.properties.exception.PropertyMissingException;
+import static org.onap.policy.common.utils.properties.SpecPropertyConfiguration.*;
+
+/**
+ *
+ */
+public class SpecPropertyConfigurationTest {
+
+ /**
+ * The specializer.
+ */
+ private static final String SPEC = "my.name";
+
+ /**
+ * Properties used when invoking constructors.
+ */
+ private Properties props;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ props = new Properties();
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#getRawPropertyValue(java.util.Properties, java.lang.String)}.
+ * @throws PropertyException
+ */
+ @Test
+ public void testGetRawPropertyValue() throws PropertyException {
+ class Config extends SpecPropertyConfiguration {
+
+ // no spec
+ @Property(name = "prefix.suffix")
+ private String noSpec;
+
+ // no spec, other type
+ @Property(name = "no.spec.bool")
+ private boolean noSpecBool;
+
+ // type 1, no prefix
+ @Property(name = "{$}.suffix")
+ private String type1NoPrefix;
+
+ // type 1, no suffix
+ @Property(name = "prefix.{$}")
+ private String type1NoSuffix;
+
+ // type 1, both prefix and suffix
+ @Property(name = "prefix.{$}.suffix")
+ private String type1Both;
+
+ // type 1, other type
+ @Property(name = "an.{$}.int")
+ private int type1Int;
+
+ // type 2, no prefix
+ @Property(name = "{abc.?.def}.suffix")
+ private String type2NoPrefix;
+
+ // type 2, no suffix
+ @Property(name = "prefix.{abc.?.def}")
+ private String type2NoSuffix;
+
+ // type 2, no spec prefix
+ @Property(name = "prefix.{?.def}.suffix")
+ private String type2NoSpecPrefix;
+
+ // type 2, no spec suffix
+ @Property(name = "prefix{.abc.?}.suffix")
+ private String type2NoSpecSuffix;
+
+ // type 2, all components
+ @Property(name = "prefix.{abc.?.def.}suffix")
+ private String type2Both;
+
+ // type 2, other type
+ @Property(name = "a.{abc.?.def.}long")
+ private long type2Long;
+
+ public Config(String specialization, Properties props) throws PropertyException {
+ super(specialization, props);
+ }
+ };
+
+ props.setProperty("prefix.suffix", "no.spec");
+ props.setProperty("no.spec.bool", "true");
+ props.setProperty("world.suffix", "type1.no.prefix");
+ props.setProperty("prefix.world", "type1.no.suffix");
+ props.setProperty("prefix.world.suffix", "type1.both");
+ props.setProperty("an.world.int", "200");
+ props.setProperty("abc.world.def.suffix", "type2.no.prefix");
+ props.setProperty("prefix.abc.world.def", "type2.no.suffix");
+ props.setProperty("prefix.world.def.suffix", "type2.no.spec.prefix");
+ props.setProperty("prefix.abc.world.suffix", "type2.no.spec.suffix");
+ props.setProperty("prefix.abc.world.def.suffix", "type2.both");
+ props.setProperty("a.abc.world.def.long", "3000");
+
+ Config cfg = new Config("world", props);
+
+ assertEquals("no.spec", cfg.noSpec);
+ assertEquals(true, cfg.noSpecBool);
+ assertEquals("type1.no.prefix", cfg.type1NoPrefix);
+ assertEquals("type1.no.suffix", cfg.type1NoSuffix);
+ assertEquals("type1.both", cfg.type1Both);
+ assertEquals(200, cfg.type1Int);
+ assertEquals("type2.no.prefix", cfg.type2NoPrefix);
+ assertEquals("type2.no.suffix", cfg.type2NoSuffix);
+ assertEquals("type2.no.spec.prefix", cfg.type2NoSpecPrefix);
+ assertEquals("type2.no.spec.suffix", cfg.type2NoSpecSuffix);
+ assertEquals("type2.both", cfg.type2Both);
+ assertEquals(3000L, cfg.type2Long);
+ }
+ @Test
+ public void testGetRawPropertyValue_Type2_Generalized() throws PropertyException {
+ class Config extends SpecPropertyConfiguration {
+
+ // type 2, all components
+ @Property(name = "prefix.{abc.?.def.}suffix")
+ private String value;
+
+ public Config(String specialization, Properties props) throws PropertyException {
+ super(specialization, props);
+ }
+ };
+
+ props.setProperty("prefix.suffix", "no.spec");
+
+ Config cfg = new Config("world", props);
+
+ assertEquals("no.spec", cfg.value);
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#getRawPropertyValue(java.util.Properties, java.lang.String)}.
+ * @throws PropertyException
+ */
+ @Test(expected = PropertyMissingException.class)
+ public void testGetRawPropertyValue_NotFound() throws PropertyException {
+ class Config extends SpecPropertyConfiguration {
+
+ @Property(name = "not.found")
+ private String notFound;
+
+ public Config(String specialization, Properties props) throws PropertyException {
+ super(specialization, props);
+ }
+ };
+
+ new Config("not found", props);
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#SpecPropertyConfiguration(java.lang.String)}.
+ * @throws PropertyException
+ */
+ @Test
+ public void testSpecPropertyConfigurationString() throws PropertyException {
+ final String propnm = "string.{$}.prop";
+ final String propval = "hello";
+
+ class Config extends SpecPropertyConfiguration {
+
+ @Property(name = propnm)
+ private String value;
+
+ public Config(String specialization) {
+ super(specialization);
+ }
+ };
+
+ props.setProperty(specialize(propnm, SPEC), propval);
+
+ Config cfg = new Config(SPEC);
+ assertEquals(null, cfg.value);
+
+ cfg.setAllFields(props);
+ assertEquals(propval, cfg.value);
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#SpecPropertyConfiguration(java.lang.String, java.util.Properties)}.
+ * @throws PropertyException
+ */
+ @Test
+ public void testSpecPropertyConfigurationStringProperties() throws PropertyException {
+ final String propnm = "int.{$}.prop";
+ final int propval = 10;
+
+ class Config extends SpecPropertyConfiguration {
+
+ @Property(name = propnm)
+ private int value;
+
+ public Config(String specialization, Properties props) throws PropertyException {
+ super(specialization, props);
+ }
+ };
+
+ props.setProperty(specialize(propnm, SPEC), String.valueOf(propval));
+
+ Config cfg = new Config(SPEC, props);
+
+ assertEquals(propval, cfg.value);
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalize(java.lang.String)}.
+ */
+ @Test
+ public void testGeneralize_NoSpec() {
+ final String xyzPdq = "xyz.pdq";
+
+ // no spec
+ assertEquals(xyzPdq, generalize(xyzPdq));
+
+ // spec type 1 throws an exception - we'll test it separately
+
+ // spec type 2
+ assertEquals(xyzPdq, generalize("xyz.{xxx.?.yyy.}pdq"));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalize(java.lang.String)}.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testGeneralize_Spec1() {
+ generalize("abc.{$}.def");
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalizeType2(java.lang.String, java.util.regex.Matcher)}.
+ */
+ @Test
+ public void testGeneralizeType2() {
+ assertEquals("abc.def", generalize("abc.{xyz?pdq}def"));
+
+ assertEquals("", generalize("{xyz?pdq}"));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specialize(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testSpecialize() {
+ final String spec = "get.spec";
+ final String abcDef = "abc.def";
+
+ // no spec
+ assertEquals(abcDef, specialize(abcDef, spec));
+
+ // spec type 1
+ assertEquals("abc.get.spec.def", specialize("abc.{$}.def", spec));
+
+ // spec type 2
+ assertEquals("abc.xxx.get.spec.yyy.def", specialize("abc.{xxx.?.yyy.}def", spec));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specializeType1(java.lang.String, java.lang.String, java.util.regex.Matcher)}.
+ */
+ @Test
+ public void testSpecializeType1() {
+ final String spec = "spec1";
+
+ // no prefix
+ assertEquals("spec1.def", specialize("{$}.def", spec));
+
+ // no suffix
+ assertEquals("abc.spec1", specialize("abc.{$}", spec));
+
+ // with both prefix and suffix
+ assertEquals("abc.spec1.def", specialize("abc.{$}.def", spec));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specializeType2(java.lang.String, java.lang.String, java.util.regex.Matcher)}.
+ */
+ @Test
+ public void testSpecializeType2() {
+ final String spec = "spec2";
+
+ // no prefix
+ assertEquals("xxx.spec2.yyy.def", specialize("{xxx.?.yyy.}def", spec));
+
+ // no suffix
+ assertEquals("abc.xxx.spec2.yyy", specialize("abc{.xxx.?.yyy}", spec));
+
+ // no spec prefix
+ assertEquals("abc.spec2.yyy.def", specialize("abc.{?.yyy.}def", spec));
+
+ // no spec suffix
+ assertEquals("abc.xxx.spec2.def", specialize("abc.{xxx.?}.def", spec));
+
+ // no components
+ assertEquals(spec, specialize("{?}", spec));
+
+ // all components
+ assertEquals("abc.xxx.spec2.yyy.def", specialize("abc.{xxx.?.yyy.}def", spec));
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/BasicPropertyExceptionTester.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/BasicPropertyExceptionTester.java
new file mode 100644
index 00000000..1e5fcfcd
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/BasicPropertyExceptionTester.java
@@ -0,0 +1,130 @@
+/*
+ * ============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.exception;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import org.hamcrest.CoreMatchers;
+
+/**
+ * Superclass used to test subclasses of {@link PropertyException}.
+ */
+public class BasicPropertyExceptionTester {
+
+ /**
+ * The "message" that's passed each time an exception is constructed.
+ */
+ protected static final String MESSAGE = "some error";
+
+ /**
+ * The "throwable" that's passed each time an exception is constructed.
+ */
+ protected static final Throwable THROWABLE = new Throwable();
+
+ /**
+ * Name of the "property" to be passed each time an exception is constructed.
+ */
+ protected static final String PROPERTY = "myName";
+
+ /**
+ * Name of the "property" field.
+ */
+ protected static final String FIELD = "PROPERTY";
+
+ /*
+ * Methods to perform various tests on the except subclass.
+ */
+
+ protected void doTestPropertyExceptionStringField_AllPopulated(PropertyException ex) {
+ standardTests(ex);
+ }
+
+ protected void doTestPropertyExceptionStringField_NullProperty(PropertyException ex) {
+ assertEquals(null, ex.getPropertyName());
+ assertEquals(FIELD, ex.getFieldName());
+ assertNotNull(ex.getMessage());
+ assertNotNull(ex.toString());
+ }
+
+ protected void doTestPropertyExceptionStringField_NullField(PropertyException ex) {
+ assertEquals(PROPERTY, ex.getPropertyName());
+ assertEquals(null, ex.getFieldName());
+ assertNotNull(ex.getMessage());
+ assertNotNull(ex.toString());
+ }
+
+ protected void doTestPropertyExceptionStringField_BothNull(PropertyException ex) {
+ assertEquals(null, ex.getPropertyName());
+ assertEquals(null, ex.getFieldName());
+ assertNotNull(ex.getMessage());
+ assertNotNull(ex.toString());
+ }
+
+ protected void doTestPropertyExceptionStringFieldString(PropertyException ex) {
+ standardTests(ex);
+ standardMessageTests(ex);
+ }
+
+ protected void doTestPropertyExceptionStringFieldThrowable(PropertyException ex) {
+ standardTests(ex);
+ standardThrowableTests(ex);
+ }
+
+ protected void doTestPropertyExceptionStringFieldStringThrowable(PropertyException ex) {
+ standardTests(ex);
+ standardMessageTests(ex);
+ standardThrowableTests(ex);
+ }
+
+ /**
+ * Performs standard tests that should apply to all subclasses.
+ *
+ * @param ex exception to test
+ */
+ protected void standardTests(PropertyException ex) {
+ assertEquals(PROPERTY, ex.getPropertyName());
+ assertEquals(FIELD, ex.getFieldName());
+ assertNotNull(ex.getMessage());
+ assertNotNull(ex.toString());
+ }
+
+ /**
+ * Performs standard tests for exceptions that were provided a message in their
+ * constructor.
+ *
+ * @param ex exception to test
+ */
+ protected void standardMessageTests(PropertyException ex) {
+ assertThat(ex.getMessage(), CoreMatchers.endsWith(MESSAGE));
+ }
+
+ /**
+ * Performs standard tests for exceptions that were provided a throwable in their
+ * constructor.
+ *
+ * @param ex exception to test
+ */
+ protected void standardThrowableTests(PropertyException ex) {
+ assertEquals(THROWABLE, ex.getCause());
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAccessExceptionTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAccessExceptionTest.java
new file mode 100644
index 00000000..304df9df
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAccessExceptionTest.java
@@ -0,0 +1,66 @@
+/*
+ * ============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.exception;
+
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PropertyAccessExceptionTest extends BasicPropertyExceptionTester {
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.exception.PropertyAccessException#PropertyAccessException(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyAccessExceptionStringField() {
+ doTestPropertyExceptionStringField_AllPopulated( new PropertyAccessException(PROPERTY, FIELD));
+ doTestPropertyExceptionStringField_NullProperty( new PropertyAccessException(null, FIELD));
+ doTestPropertyExceptionStringField_NullField( new PropertyAccessException(PROPERTY, null));
+ doTestPropertyExceptionStringField_BothNull( new PropertyAccessException(null, null));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.exception.PropertyAccessException#PropertyAccessException(java.lang.String, java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyAccessExceptionStringFieldString() {
+ doTestPropertyExceptionStringFieldString(new PropertyAccessException(PROPERTY, FIELD, MESSAGE));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.exception.PropertyAccessException#PropertyAccessException(java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyAccessExceptionStringFieldThrowable() {
+ doTestPropertyExceptionStringFieldThrowable(new PropertyAccessException(PROPERTY, FIELD, THROWABLE));
+ }
+
+ /**
+ * Test method for {@link org.onap.policy.common.utils.properties.exception.PropertyAccessException#PropertyAccessException(java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyAccessExceptionStringFieldStringThrowable() {
+ doTestPropertyExceptionStringFieldStringThrowable(
+ new PropertyAccessException(PROPERTY, FIELD, MESSAGE, THROWABLE));
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationExceptionTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationExceptionTest.java
new file mode 100644
index 00000000..2c033084
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyAnnotationExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * ============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.exception;
+
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PropertyAnnotationExceptionTest extends BasicPropertyExceptionTester {
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringField() {
+ doTestPropertyExceptionStringField_AllPopulated(new PropertyAnnotationException(PROPERTY, FIELD));
+ doTestPropertyExceptionStringField_NullProperty(new PropertyAnnotationException(null, FIELD));
+ doTestPropertyExceptionStringField_NullField(new PropertyAnnotationException(PROPERTY, null));
+ doTestPropertyExceptionStringField_BothNull(new PropertyAnnotationException(null, null));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldString() {
+ doTestPropertyExceptionStringFieldString(new PropertyAnnotationException(PROPERTY, FIELD, MESSAGE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldThrowable() {
+ doTestPropertyExceptionStringFieldThrowable(new PropertyAnnotationException(PROPERTY, FIELD, THROWABLE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldStringThrowable() {
+ doTestPropertyExceptionStringFieldStringThrowable(
+ new PropertyAnnotationException(PROPERTY, FIELD, MESSAGE, THROWABLE));
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyExceptionTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyExceptionTest.java
new file mode 100644
index 00000000..18186ba2
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyExceptionTest.java
@@ -0,0 +1,69 @@
+/*
+ * ============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.exception;
+
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PropertyExceptionTest extends BasicPropertyExceptionTester {
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringField() {
+ doTestPropertyExceptionStringField_AllPopulated(new PropertyException(PROPERTY, FIELD));
+ doTestPropertyExceptionStringField_NullProperty(new PropertyException(null, FIELD));
+ doTestPropertyExceptionStringField_NullField(new PropertyException(PROPERTY, null));
+ doTestPropertyExceptionStringField_BothNull(new PropertyException(null, null));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldString() {
+ doTestPropertyExceptionStringFieldString(new PropertyException(PROPERTY, FIELD, MESSAGE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldThrowable() {
+ doTestPropertyExceptionStringFieldThrowable(new PropertyException(PROPERTY, FIELD, THROWABLE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldStringThrowable() {
+ doTestPropertyExceptionStringFieldStringThrowable(new PropertyException(PROPERTY, FIELD, MESSAGE, THROWABLE));
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidExceptionTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidExceptionTest.java
new file mode 100644
index 00000000..f83b7ed3
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyInvalidExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * ============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.exception;
+
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PropertyInvalidExceptionTest extends BasicPropertyExceptionTester {
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringField() {
+ doTestPropertyExceptionStringField_AllPopulated(new PropertyInvalidException(PROPERTY, FIELD));
+ doTestPropertyExceptionStringField_NullProperty(new PropertyInvalidException(null, FIELD));
+ doTestPropertyExceptionStringField_NullField(new PropertyInvalidException(PROPERTY, null));
+ doTestPropertyExceptionStringField_BothNull(new PropertyInvalidException(null, null));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldString() {
+ doTestPropertyExceptionStringFieldString(new PropertyInvalidException(PROPERTY, FIELD, MESSAGE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldThrowable() {
+ doTestPropertyExceptionStringFieldThrowable(new PropertyInvalidException(PROPERTY, FIELD, THROWABLE));
+ }
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable)}.
+ */
+ @Test
+ public void testPropertyExceptionStringFieldStringThrowable() {
+ doTestPropertyExceptionStringFieldStringThrowable(
+ new PropertyInvalidException(PROPERTY, FIELD, MESSAGE, THROWABLE));
+ }
+
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyMissingExceptionTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyMissingExceptionTest.java
new file mode 100644
index 00000000..f99ddd93
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/properties/exception/PropertyMissingExceptionTest.java
@@ -0,0 +1,42 @@
+/*
+ * ============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.exception;
+
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PropertyMissingExceptionTest extends BasicPropertyExceptionTester {
+
+ /**
+ * Test method for
+ * {@link org.onap.policy.common.utils.properties.exception.PropertyException#PropertyException(java.lang.String, java.lang.String)}.
+ */
+ @Test
+ public void testPropertyExceptionStringField() {
+ doTestPropertyExceptionStringField_AllPopulated(new PropertyMissingException(PROPERTY, FIELD));
+ doTestPropertyExceptionStringField_NullProperty(new PropertyMissingException(null, FIELD));
+ doTestPropertyExceptionStringField_NullField(new PropertyMissingException(PROPERTY, null));
+ doTestPropertyExceptionStringField_BothNull(new PropertyMissingException(null, null));
+ }
+
+}