diff options
author | liamfallon <liam.fallon@ericsson.com> | 2018-10-04 07:06:41 +0100 |
---|---|---|
committer | liamfallon <liam.fallon@ericsson.com> | 2018-10-04 16:44:57 +0100 |
commit | b213e23edf68d36b4c0984d94d38d444f96949e2 (patch) | |
tree | bd7b60df8e8220abe93094fbbe373bc9c19791d0 | |
parent | 16a8f59fed2e53b1bbedaf65a33165bb4d3225f8 (diff) |
Fix creation of sub Avro items
When an Avro object is created, nested sub avro objects
are not created automatically. This change allows
invocation of a method to create sub objects of Avro
objects.
Issue-ID: POLICY-954
Change-Id: Ie510867f2631ba6a8c6f5452c08aef5db67e8997
Signed-off-by: liamfallon <liam.fallon@ericsson.com>
7 files changed, 186 insertions, 30 deletions
diff --git a/context/context-management/src/main/java/org/onap/policy/apex/context/SchemaHelper.java b/context/context-management/src/main/java/org/onap/policy/apex/context/SchemaHelper.java index 44ec06c6d..b6d21a20d 100644 --- a/context/context-management/src/main/java/org/onap/policy/apex/context/SchemaHelper.java +++ b/context/context-management/src/main/java/org/onap/policy/apex/context/SchemaHelper.java @@ -92,6 +92,14 @@ public interface SchemaHelper { Object createNewInstance(Object incomingObject); /** + * Create an object of a sub type of this object. + * + * @param subType the sub type definition of this type + * @return a new object of the sub type + */ + Object createNewSubInstance(String subType); + + /** * Unmarshal an object in schema format into a Java object. * * @param object the object as a Java object diff --git a/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/AbstractSchemaHelper.java b/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/AbstractSchemaHelper.java index f50eb5914..6d98313ed 100644 --- a/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/AbstractSchemaHelper.java +++ b/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/AbstractSchemaHelper.java @@ -22,6 +22,7 @@ package org.onap.policy.apex.context.impl.schema; import java.lang.reflect.Constructor; +import org.apache.commons.lang3.NotImplementedException; import org.onap.policy.apex.context.ContextRuntimeException; import org.onap.policy.apex.context.SchemaHelper; import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey; @@ -169,4 +170,12 @@ public abstract class AbstractSchemaHelper implements SchemaHelper { throw new ContextRuntimeException(returnString); } } + + /* (non-Javadoc) + * @see org.onap.policy.apex.context.SchemaHelper#createNewSubInstance(java.lang.String) + */ + @Override + public Object createNewSubInstance(String subType) { + throw new NotImplementedException("sub types are not supported on this schema helper"); + } } diff --git a/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelper.java b/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelper.java index 8b61f718a..fcc7c4d60 100644 --- a/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelper.java +++ b/context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelper.java @@ -279,5 +279,4 @@ public class JavaSchemaHelper extends AbstractSchemaHelper { return gsonBuilder.create(); } - } diff --git a/context/context-management/src/test/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelperInstanceCreationTest.java b/context/context-management/src/test/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelperInstanceCreationTest.java index 800a7011d..14d44ee8b 100644 --- a/context/context-management/src/test/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelperInstanceCreationTest.java +++ b/context/context-management/src/test/java/org/onap/policy/apex/context/impl/schema/java/JavaSchemaHelperInstanceCreationTest.java @@ -96,7 +96,6 @@ public class JavaSchemaHelperInstanceCreationTest { } assertEquals(true, schemaHelper0.createNewInstance("true")); - try { schemaHelper1.createNewInstance(); fail("this test should throw an exception here"); @@ -108,5 +107,12 @@ public class JavaSchemaHelperInstanceCreationTest { assertEquals("", schemaHelper2.createNewInstance()); assertEquals("true", schemaHelper2.createNewInstance("true")); + + try { + schemaHelper1.createNewSubInstance("SomeSubtype"); + fail("this test should throw an exception here"); + } catch (final Exception e) { + assertEquals("sub types are not supported on this schema helper", e.getMessage()); + } } } diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelper.java index 015d9ea30..723aefdc5 100644 --- a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelper.java +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelper.java @@ -25,8 +25,12 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import java.io.ByteArrayOutputStream; +import java.util.LinkedHashSet; +import java.util.Set; import org.apache.avro.Schema; +import org.apache.avro.Schema.Field; +import org.apache.avro.Schema.Type; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.generic.GenericRecord; @@ -37,6 +41,7 @@ import org.apache.avro.io.JsonDecoder; import org.apache.avro.io.JsonEncoder; import org.onap.policy.apex.context.ContextRuntimeException; import org.onap.policy.apex.context.impl.schema.AbstractSchemaHelper; +import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey; import org.onap.policy.apex.model.basicmodel.concepts.AxKey; import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema; import org.slf4j.ext.XLogger; @@ -101,7 +106,7 @@ public class AvroSchemaHelper extends AbstractSchemaHelper { // Create a new instance using the Avro object mapper final Object newInstance = avroObjectMapper.createNewInstance(avroSchema); - // If no new instance is created, use default schema handler behavior + // If no new instance is created, use default schema handler behaviour if (newInstance != null) { return newInstance; } else { @@ -130,6 +135,87 @@ public class AvroSchemaHelper extends AbstractSchemaHelper { } @Override + public Object createNewSubInstance(final String subInstanceType) { + final Set<String> foundTypes = new LinkedHashSet<>(); + + Object subInstance = createNewSubInstance(avroSchema, subInstanceType, foundTypes); + + if (subInstance != null) { + return subInstance; + } else { + final String returnString = getUserKey().getId() + ": the schema \"" + avroSchema.getName() + + "\" does not have a subtype of type \"" + subInstanceType + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + } + + /** + * Create an instance of a sub type of this type. + * + * @param schema the Avro schema of the the type + * @param subInstanceType the sub type + * @param foundTypes types we have already found + * @return the sub type schema or null if it is not created + */ + private Object createNewSubInstance(Schema schema, String subInstanceType, final Set<String> foundTypes) { + // Try Array element types + if (Type.ARRAY == schema.getType()) { + Object newInstance = instantiateSubInstance(subInstanceType, schema.getElementType(), foundTypes); + if (newInstance != null) { + return newInstance; + } + } + + if (Type.MAP == schema.getType()) { + Object newInstance = instantiateSubInstance(subInstanceType, schema.getValueType(), foundTypes); + if (newInstance != null) { + return newInstance; + } + } + + if (Type.RECORD == schema.getType()) { + for (Field field : schema.getFields()) { + Object newInstance = instantiateSubInstance(subInstanceType, field.schema(), foundTypes); + if (newInstance != null) { + return newInstance; + } + } + } + + return null; + } + + /** + * Instantiate a sub instance of a type. + * + * @param subInstanceType the type of the sub instance to create + * @param subSchema the sub schema we have received + * @param foundTypes types we have already found + * @return an instance of the type or null if it is the incorrect type + */ + private Object instantiateSubInstance(final String subInstanceType, final Schema subSchema, + final Set<String> foundTypes) { + if (subSchema == null) { + return null; + } + + // Check for recursive use of field names in records, if we have already checked a field name + // skip it this time. + if (foundTypes.contains(subSchema.getName())) { + return null; + } + + foundTypes.add(subSchema.getName()); + + if (subSchema.getName().equals(subInstanceType)) { + return new AvroObjectMapperFactory().get(AxArtifactKey.getNullKey(), subSchema) + .createNewInstance(subSchema); + } + return createNewSubInstance(subSchema, subInstanceType, foundTypes); + } + + @Override public Object unmarshal(final Object object) { // If an object is already in the correct format, just carry on if (passThroughObject(object)) { diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaMap.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaMap.java index 9bc87cf61..37069be47 100644 --- a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaMap.java +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaMap.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.util.HashMap; +import org.apache.avro.generic.GenericRecord; import org.apache.avro.util.Utf8; import org.junit.After; import org.junit.Before; @@ -46,7 +47,7 @@ import org.onap.policy.common.parameters.ParameterService; * The Class TestAvroSchemaMap. * * @author Liam Fallon (liam.fallon@ericsson.com) - * @version + * @version */ public class TestAvroSchemaMap { private final AxKey testKey = new AxArtifactKey("AvroTest", "0.0.1"); @@ -66,8 +67,8 @@ public class TestAvroSchemaMap { ModelService.registerModel(AxContextSchemas.class, schemas); longMapSchema = TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleLong.avsc"); addressMapSchema = TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleAddress.avsc"); - addressMapSchemaInvalidFields = - TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleAddressInvalidFields.avsc"); + addressMapSchemaInvalidFields = TextFileUtils + .getTextFileAsString("src/test/resources/avsc/MapExampleAddressInvalidFields.avsc"); } /** @@ -79,7 +80,7 @@ public class TestAvroSchemaMap { schemaParameters.setName(ContextParameterConstants.SCHEMA_GROUP_NAME); schemaParameters.getSchemaHelperParameterMap().put("AVRO", new AvroSchemaHelperParameters()); ParameterService.register(schemaParameters); - + } /** @@ -97,8 +98,8 @@ public class TestAvroSchemaMap { */ @Test public void testMapInit() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", addressMapSchema); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + addressMapSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -110,7 +111,7 @@ public class TestAvroSchemaMap { final HashMap<?, ?> newMapFull = (HashMap<?, ?>) schemaHelper.createNewInstance(inString); assertEquals("{\"streetaddress\": \"221 B Baker St.\", \"city\": \"London\"}", - newMapFull.get(new Utf8("address2")).toString()); + newMapFull.get(new Utf8("address2")).toString()); } /** @@ -120,8 +121,8 @@ public class TestAvroSchemaMap { */ @Test public void testLongMapUnmarshalMarshal() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", longMapSchema); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", + longMapSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -137,8 +138,8 @@ public class TestAvroSchemaMap { */ @Test public void testAddressMapUnmarshalMarshal() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", addressMapSchema); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", + addressMapSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -148,14 +149,31 @@ public class TestAvroSchemaMap { } /** + * Test sub record create. + * + * @throws IOException Signals that an I/O exception has occurred. + */ + @Test + public void testSubRecordCreateRecord() throws IOException { + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", + addressMapSchema); + + schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); + final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); + + GenericRecord subRecord = (GenericRecord) schemaHelper.createNewSubInstance("AddressUSRecord"); + assertEquals(null, subRecord.get("streetAddress")); + } + + /** * Test address map unmarshal marshal invalid fields. * * @throws IOException Signals that an I/O exception has occurred. */ @Test public void testAddressMapUnmarshalMarshalInvalidFields() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", addressMapSchemaInvalidFields); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", + addressMapSchemaInvalidFields); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaRecord.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaRecord.java index 6b1d09eb6..b79a5cd91 100644 --- a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaRecord.java +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/test/java/org/onap/policy/apex/plugins/context/schema/avro/TestAvroSchemaRecord.java @@ -21,6 +21,7 @@ package org.onap.policy.apex.plugins.context.schema.avro; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import java.io.IOException; @@ -28,6 +29,7 @@ import org.apache.avro.generic.GenericRecord; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onap.policy.apex.context.ContextRuntimeException; import org.onap.policy.apex.context.SchemaHelper; import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory; import org.onap.policy.apex.context.parameters.ContextParameterConstants; @@ -45,7 +47,7 @@ import org.onap.policy.common.parameters.ParameterService; * The Class TestAvroSchemaRecord. * * @author Liam Fallon (liam.fallon@ericsson.com) - * @version + * @version */ public class TestAvroSchemaRecord { private final AxKey testKey = new AxArtifactKey("AvroTest", "0.0.1"); @@ -67,8 +69,8 @@ public class TestAvroSchemaRecord { recordSchema = TextFileUtils.getTextFileAsString("src/test/resources/avsc/RecordExample.avsc"); recordSchemaVpn = TextFileUtils.getTextFileAsString("src/test/resources/avsc/RecordExampleVPN.avsc"); recordSchemaVpnReuse = TextFileUtils.getTextFileAsString("src/test/resources/avsc/RecordExampleVPNReuse.avsc"); - recordSchemaInvalidFields = - TextFileUtils.getTextFileAsString("src/test/resources/avsc/RecordExampleInvalidFields.avsc"); + recordSchemaInvalidFields = TextFileUtils + .getTextFileAsString("src/test/resources/avsc/RecordExampleInvalidFields.avsc"); } /** @@ -80,7 +82,7 @@ public class TestAvroSchemaRecord { schemaParameters.setName(ContextParameterConstants.SCHEMA_GROUP_NAME); schemaParameters.getSchemaHelperParameterMap().put("AVRO", new AvroSchemaHelperParameters()); ParameterService.register(schemaParameters); - + } /** @@ -98,8 +100,8 @@ public class TestAvroSchemaRecord { */ @Test public void testRecordInit() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", recordSchema); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -119,8 +121,8 @@ public class TestAvroSchemaRecord { */ @Test public void testRecordUnmarshalMarshal() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", recordSchema); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -130,14 +132,42 @@ public class TestAvroSchemaRecord { } /** + * Test record create. + * + * @throws IOException Signals that an I/O exception has occurred. + */ + @Test + public void testRecordCreateRecord() throws IOException { + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchema); + + schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); + final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); + + GenericRecord subRecord = (GenericRecord) schemaHelper.createNewSubInstance("AddressUSRecord"); + assertEquals(null, subRecord.get("streetAddress")); + + subRecord = (GenericRecord) schemaHelper.createNewSubInstance("EmailAddress"); + assertEquals(null, subRecord.get("address")); + + try { + subRecord = (GenericRecord) schemaHelper.createNewSubInstance("IDontExist"); + fail("test should throw an exception here"); + } catch (ContextRuntimeException cre) { + assertEquals("AvroTest:0.0.1: the schema \"User\" does not have a subtype of type \"IDontExist\"", + cre.getMessage()); + } + } + + /** * Test record unmarshal marshal invalid. * * @throws IOException Signals that an I/O exception has occurred. */ @Test public void testRecordUnmarshalMarshalInvalid() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", recordSchemaInvalidFields); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchemaInvalidFields); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -152,8 +182,8 @@ public class TestAvroSchemaRecord { */ @Test public void testVpnRecordUnmarshalMarshal() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", recordSchemaVpn); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchemaVpn); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey()); @@ -168,8 +198,8 @@ public class TestAvroSchemaRecord { */ @Test public void testVpnRecordReuse() throws IOException { - final AxContextSchema avroSchema = - new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", recordSchemaVpnReuse); + final AxContextSchema avroSchema = new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", + recordSchemaVpnReuse); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema); |