From b213e23edf68d36b4c0984d94d38d444f96949e2 Mon Sep 17 00:00:00 2001 From: liamfallon Date: Thu, 4 Oct 2018 07:06:41 +0100 Subject: 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 --- .../context/schema/avro/AvroSchemaHelper.java | 88 +++++++++++++++++++++- .../context/schema/avro/TestAvroSchemaMap.java | 44 +++++++---- .../context/schema/avro/TestAvroSchemaRecord.java | 58 ++++++++++---- 3 files changed, 162 insertions(+), 28 deletions(-) (limited to 'plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src') 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 { @@ -129,6 +134,87 @@ public class AvroSchemaHelper extends AbstractSchemaHelper { } } + @Override + public Object createNewSubInstance(final String subInstanceType) { + final Set 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 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 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 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()); @@ -147,6 +148,23 @@ public class TestAvroSchemaMap { testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleAddressFull.json"); } + /** + * 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. * @@ -154,8 +172,8 @@ public class TestAvroSchemaMap { */ @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()); @@ -129,6 +131,34 @@ public class TestAvroSchemaRecord { testUnmarshalMarshal(schemaHelper, "src/test/resources/data/RecordExampleFull.json"); } + /** + * 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. * @@ -136,8 +166,8 @@ public class TestAvroSchemaRecord { */ @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); -- cgit 1.2.3-korg