diff options
Diffstat (limited to 'plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main')
13 files changed, 1383 insertions, 0 deletions
diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroArrayObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroArrayObjectMapper.java new file mode 100644 index 000000000..56d84251f --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroArrayObjectMapper.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericData.Array; + +/** + * Object mapper for arrays, uses default behaviour except for a specific default constructor + * implementation. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroArrayObjectMapper extends AvroDirectObjectMapper { + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache.avro.Schema) + */ + @SuppressWarnings("rawtypes") + @Override + public Object createNewInstance(final Schema avroSchema) { + return new Array(0, avroSchema); + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroBytesObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroBytesObjectMapper.java new file mode 100644 index 000000000..dc281a52b --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroBytesObjectMapper.java @@ -0,0 +1,149 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.nio.ByteBuffer; + +import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; +import org.onap.policy.apex.context.ContextRuntimeException; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class does string mapping from the Avro BYTES type to a Java byte array. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroBytesObjectMapper implements AvroObjectMapper { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroBytesObjectMapper.class); + + // The user keyAvro type for direct mapping + private AxKey userKey; + private Type avroType; + + // The Apex compatible class + private final Class<Byte[]> schemaClass = Byte[].class; + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getJavaClass() + */ + @Override + public Class<?> getJavaClass() { + return schemaClass; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#setAvroType(org.apache. + * avro. Schema.Type) + */ + @Override + public void init(final AxKey intUserKey, final Type initAvroType) { + this.userKey = intUserKey; + this.avroType = initAvroType; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache. avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + // By default, we do not create an instance, normal Java object creation for byte arrays is + // sufficient + return null; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getAvroType() + */ + @Override + public Type getAvroType() { + return avroType; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapFromAvro(java.lang. + * Object) + */ + @Override + public Object mapFromAvro(final Object avroObject) { + // The Avro object should be a Utf8 object + if (!(avroObject instanceof ByteBuffer)) { + final String returnString = + userKey.getID() + ": object \"" + avroObject + "\" of class \"" + avroObject.getClass() + + "\" cannot be decoded to an object of class \"" + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + // Cast the byte buffer object so we get access to its methods + final ByteBuffer byteBufferAvroObject = (ByteBuffer) avroObject; + + // read the byte buffer into a byte array + final byte[] byteArray = new byte[byteBufferAvroObject.remaining()]; + byteBufferAvroObject.get(byteArray); + + return byteArray; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapToAvro(java.lang.Object) + */ + @Override + public Object mapToAvro(final Object object) { + if (object == null) { + final String returnString = userKey.getID() + ": cannot encode a null object of class \"" + + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + // The incoming object should be a byte array + if (!(object instanceof byte[])) { + final String returnString = userKey.getID() + ": object \"" + object + "\" of class \"" + object.getClass() + + "\" cannot be decoded to an object of class \"" + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + // Create a ByteBuffer object to serialize the bytes + final ByteBuffer byteBuffer = ByteBuffer.wrap((byte[]) object); + + return byteBuffer; + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroDirectObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroDirectObjectMapper.java new file mode 100644 index 000000000..acf64c3c9 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroDirectObjectMapper.java @@ -0,0 +1,165 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; +import org.apache.avro.generic.GenericData; +import org.onap.policy.apex.context.ContextRuntimeException; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class does direct mapping from Avro classes to Java classes, used for Avro primitive types + * that directly produce Java objects. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroDirectObjectMapper implements AvroObjectMapper { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroDirectObjectMapper.class); + + // Map for Avro primitive types to Java primitive types + private static final Map<Schema.Type, Class<?>> AVRO_JAVA_TYPE_MAP = new TreeMap<>(); + + // @formatter:off + // Initialize the mapping + static { + AVRO_JAVA_TYPE_MAP.put(Schema.Type.ARRAY, GenericData.Array.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.BOOLEAN, Boolean.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.DOUBLE, Double.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.ENUM, GenericData.EnumSymbol.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.FIXED, GenericData.Fixed.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.FLOAT, Float.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.INT, Integer.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.LONG, Long.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.MAP, HashMap.class); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.NULL, null); + AVRO_JAVA_TYPE_MAP.put(Schema.Type.RECORD, GenericData.Record.class); + } + // @formatter:on + + // The user keyAvro type for direct mapping + private AxKey userKey; + private Type avroType; + + // The Apex compatible class + private Class<?> schemaClass; + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getJavaClass() + */ + @Override + public Class<?> getJavaClass() { + return schemaClass; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#setAvroType(org.apache. + * avro. Schema.Type) + */ + @Override + public void init(final AxKey initUserKey, final Type initAvroType) { + this.userKey = initUserKey; + this.avroType = initAvroType; + schemaClass = AVRO_JAVA_TYPE_MAP.get(avroType); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache. avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + // By default, we do not create an instance, normal Java object creation is sufficient + return null; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getAvroType() + */ + @Override + public Type getAvroType() { + return avroType; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapFromAvro(java.lang. + * Object) + */ + @Override + public Object mapFromAvro(final Object avroObject) { + // Always return null if the schema is a null schema + if (schemaClass == null) { + return null; + } + + // It is legal for the schema class to be null, if the Avro schema has a "null" type then + // the decoded object is always returned as a null + if (!schemaClass.isAssignableFrom(avroObject.getClass())) { + final String returnString = + userKey.getID() + ": object \"" + avroObject + "\" of class \"" + avroObject.getClass() + + "\" cannot be decoded to an object of class \"" + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + return avroObject; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapToAvro(java.lang.Object) + */ + @Override + public Object mapToAvro(final Object object) { + // Null values are only allowed if the schema class is null + if (object == null) { + if (schemaClass != null) { + final String returnString = userKey.getID() + ": cannot encode a null object of class \"" + + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + } + + // For direct mappings, just work directly with the Java objects + return object; + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroEnumObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroEnumObjectMapper.java new file mode 100644 index 000000000..ec84450d5 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroEnumObjectMapper.java @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.util.List; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericData.EnumSymbol; + +/** + * Object mapper for enums, uses default behaviour except for a specific default constructor + * implementation. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroEnumObjectMapper extends AvroDirectObjectMapper { + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache.avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + // Initialize the ENUM to the first ENUM symbol on the list + final List<String> enumSymbols = avroSchema.getEnumSymbols(); + + // Check if any ENUM symbols have been defined + if (enumSymbols == null || enumSymbols.isEmpty()) { + return null; + } + + return new EnumSymbol(avroSchema, enumSymbols.get(0)); + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroNullableMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroNullableMapper.java new file mode 100644 index 000000000..ff8806c86 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroNullableMapper.java @@ -0,0 +1,124 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; + +/** + * The Class AvroNullableMapper handles Avro null mappings to Java null values. + * + * @author John Keeney (john.keeney@ericsson.com) + */ +public class AvroNullableMapper extends AvroDirectObjectMapper { + // The wrapped mapper for nullables + private final AvroObjectMapper wrappedMapper; + + /** + * The Constructor. + * + * @param wrappedMapper the wrapped mapper + */ + public AvroNullableMapper(final AvroObjectMapper wrappedMapper) { + this.wrappedMapper = wrappedMapper; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#getJavaClass() + */ + @Override + public Class<?> getJavaClass() { + return wrappedMapper.getJavaClass(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#init(org.onap.policy. + * apex. model.basicmodel.concepts.AxKey, org.apache.avro.Schema.Type) + */ + @Override + public void init(final AxKey userKey, final Type avroType) { + wrappedMapper.init(userKey, avroType); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#createNewInstance( + * org. apache.avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + return wrappedMapper.createNewInstance(avroSchema); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#getAvroType() + */ + @Override + public Type getAvroType() { + return Schema.Type.UNION; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#mapFromAvro(java. + * lang. Object) + */ + @Override + public Object mapFromAvro(final Object avroObject) { + if (avroObject == null) { + return null; + } else { + return wrappedMapper.mapFromAvro(avroObject); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroDirectObjectMapper#mapToAvro(java.lang. + * Object) + */ + @Override + public Object mapToAvro(final Object object) { + if (object == null) { + return null; + } else { + throw new ApexRuntimeException("Unions/Nullable is not supported in output event ... Coming soon!"); + } + + } + +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapper.java new file mode 100644 index 000000000..77de5928e --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapper.java @@ -0,0 +1,78 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; + +/** + * This interface is used to allow mapping of Avro object to and from Java objects. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface AvroObjectMapper { + /** + * Get the Java class produced and consumed by this mapper. + * + * @return the Java class + */ + Class<?> getJavaClass(); + + /** + * Initialize the mapper is working with. + * + * @param userKey the user key + * @param avroType the avro type + */ + void init(AxKey userKey, Type avroType); + + /** + * Create a new instance of the java object the Avro schema maps to. + * + * @param avroSchema the Avro schema to use to create the new instance + * @return a new instance of the object + */ + Object createNewInstance(Schema avroSchema); + + /** + * Set the Avro type the mapper is working with. + * + * @return the avro type + */ + Type getAvroType(); + + /** + * Map the Avro object to an object Apex can handler. + * + * @param avroObject the Avro object to map + * @return the Apex-compatible object + */ + Object mapFromAvro(Object avroObject); + + /** + * Map the Apex object to an Avro object. + * + * @param object the Apex-compatible object + * @return the Avro object + */ + Object mapToAvro(Object object); +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapperFactory.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapperFactory.java new file mode 100644 index 000000000..22152a8da --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroObjectMapperFactory.java @@ -0,0 +1,131 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.avro.Schema; +import org.onap.policy.apex.context.ContextRuntimeException; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class maps between Avro types to Java types. This class is thread safe. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroObjectMapperFactory { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroObjectMapperFactory.class); + + // Map for Avro primitive types to Java primitive types + private static final Map<Schema.Type, Class<? extends AvroObjectMapper>> AVRO_OBJECT_MAPPER_MAP = new TreeMap<>(); + + // @formatter:off + // Initialize the mapping + static { + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.ARRAY, AvroArrayObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.BOOLEAN, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.BYTES, AvroBytesObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.DOUBLE, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.ENUM, AvroEnumObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.FIXED, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.FLOAT, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.INT, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.LONG, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.MAP, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.NULL, AvroDirectObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.RECORD, AvroRecordObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.STRING, AvroStringObjectMapper.class); + AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.UNION, null); + } + // @formatter:on + + /** + * Gets the Avro object mapper to use for an artifact with the given key and schema. + * + * @param userKey the key of the artifact + * @param incomingSchema the incoming schema + * @return the avro object mapper + */ + public AvroObjectMapper get(final AxKey userKey, final Schema incomingSchema) { + Schema schema = incomingSchema; + boolean isnullable = false; + if (Schema.Type.UNION.equals(schema.getType())) { + + final List<Schema> types = schema.getTypes(); + // TODO: properly support UNIONS + // currently only support unions with 2 types, one of which is NULL + final Schema nullschema = Schema.create(Schema.Type.NULL); + if (types.size() != 2 || !types.contains(nullschema)) { + final String resultSting = userKey.getID() + + ": Apex currently only supports UNION schemas with 2 options, one must be NULL"; + LOGGER.warn(resultSting); + throw new ContextRuntimeException(resultSting); + } + isnullable = true; + // get the non-null schema given for the union so it can be wrapped + schema = types.get(0); + if (Schema.Type.NULL.equals(schema.getType())) { + schema = types.get(1); + } + if (Schema.Type.NULL.equals(schema.getType())) { + final String resultSting = userKey.getID() + + ": Apex currently only supports UNION schema2 with 2 options, only one can be NULL, and the other cannot be another UNION"; + LOGGER.warn(resultSting); + throw new ContextRuntimeException(resultSting); + } + } + + final Schema.Type avroType = schema.getType(); + + // Check that there is a definition for the mapper for this type + if (!AVRO_OBJECT_MAPPER_MAP.containsKey(avroType) || AVRO_OBJECT_MAPPER_MAP.get(avroType) == null) { + final String resultSting = + userKey.getID() + ": no Avro object mapper defined for Avro type \"" + avroType + "\""; + LOGGER.warn(resultSting); + throw new ContextRuntimeException(resultSting); + } + + // Create a mapper + AvroObjectMapper avroObjectMapper; + try { + avroObjectMapper = AVRO_OBJECT_MAPPER_MAP.get(avroType).newInstance(); + if (isnullable) { + avroObjectMapper = new AvroNullableMapper(avroObjectMapper); + } + + } catch (final Exception e) { + final String resultSting = userKey.getID() + ": could not create an Avro object mapper of type \"" + + AVRO_OBJECT_MAPPER_MAP.get(avroType) + "\" for Avro type \"" + avroType + "\" : " + e; + LOGGER.warn(resultSting, e); + throw new ContextRuntimeException(resultSting, e); + } + + // Set the type and return + avroObjectMapper.init(userKey, avroType); + + return avroObjectMapper; + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroRecordObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroRecordObjectMapper.java new file mode 100644 index 000000000..9d24a9a86 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroRecordObjectMapper.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericData.Record; + +/** + * Object mapper for records, uses default behaviour except for a specific default constructor + * implementation. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroRecordObjectMapper extends AvroDirectObjectMapper { + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache. avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + return new Record(avroSchema); + } +} 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 new file mode 100644 index 000000000..5fba274ce --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelper.java @@ -0,0 +1,261 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.io.ByteArrayOutputStream; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumWriter; +import org.apache.avro.io.DecoderFactory; +import org.apache.avro.io.EncoderFactory; +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.AxKey; +import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +/** + * This class is the implementation of the {@link org.onap.policy.apex.context.SchemaHelper} + * interface for Avro schemas. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroSchemaHelper extends AbstractSchemaHelper { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroSchemaHelper.class); + + // The Avro schema for this context schema + private Schema avroSchema; + + // The mapper that translates between Java and Avro objects + private AvroObjectMapper avroObjectMapper; + + @Override + public void init(final AxKey userKey, final AxContextSchema schema) throws ContextRuntimeException { + super.init(userKey, schema); + + // Configure the Avro schema + try { + avroSchema = new Schema.Parser().parse(schema.getSchema()); + } catch (final Exception e) { + final String resultSting = userKey.getID() + ": avro context schema \"" + schema.getID() + + "\" schema is invalid: " + e.getMessage() + ", schema: " + schema.getSchema(); + LOGGER.warn(resultSting); + throw new ContextRuntimeException(resultSting); + } + + // Get the object mapper for the schema type to a Java class + avroObjectMapper = new AvroObjectMapperFactory().get(userKey, avroSchema); + + // Get the Java type for this schema, if it is a primitive type then we can do direct + // conversion to JAva + setSchemaClass(avroObjectMapper.getJavaClass()); + } + + /** + * Getter to get the Avro schema. + * + * @return the Avro schema + */ + public Schema getAvroSchema() { + return avroSchema; + } + + @Override + public Object getSchemaObject() { + return avroSchema; + } + + @Override + public Object createNewInstance() { + // 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 (newInstance != null) { + return newInstance; + } else { + return super.createNewInstance(); + } + } + + @Override + public Object createNewInstance(final String stringValue) { + return unmarshal(stringValue); + } + + @Override + public Object createNewInstance(final Object incomingObject) { + if (incomingObject instanceof JsonElement) { + final Gson gson = new GsonBuilder().serializeNulls().create(); + final String elementJsonString = gson.toJson((JsonElement) incomingObject); + + return createNewInstance(elementJsonString); + } + else { + final String returnString = getUserKey().getID() + ": the object \"" + incomingObject + + "\" is not an instance of JsonObject"; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + } + + @Override + public Object unmarshal(final Object object) { + // If an object is already in the correct format, just carry on + if (passThroughObject(object)) { + return object; + } + + String objectString = getStringObject(object); + + // Translate illegal characters in incoming JSON keys to legal Avro values + objectString = AvroSchemaKeyTranslationUtilities.translateIllegalKeys(objectString, false); + + // Decode the object + Object decodedObject; + try { + final JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(avroSchema, objectString); + decodedObject = new GenericDatumReader<GenericRecord>(avroSchema).read(null, jsonDecoder); + } catch (final Exception e) { + final String returnString = getUserKey().getID() + ": object \"" + objectString + + "\" Avro unmarshalling failed: " + e.getMessage(); + LOGGER.warn(returnString, e); + throw new ContextRuntimeException(returnString, e); + } + + // Now map the decoded object into something we can handle + return avroObjectMapper.mapFromAvro(decodedObject); + } + + /** + * Check that the incoming object is a string, the incoming object must be a string containing + * Json + * + * @param object incoming object + * @return object as String + */ + private String getStringObject(final Object object) { + try { + if (isObjectString(object)) { + String objectString = object.toString().trim(); + if (objectString.length() == 0) { + return "\"\""; + } else if (objectString.length() == 1) { + return "\"" + objectString + "\""; + } else { + // All strings must be quoted for decoding + if (objectString.charAt(0) != '"') { + objectString = '"' + objectString; + } + if (objectString.charAt(objectString.length() - 1) != '"') { + objectString += '"'; + } + } + return objectString; + } else { + return (String) object; + } + } catch (final ClassCastException e) { + final String returnString = getUserKey().getID() + ": object \"" + object + "\" of type \"" + + (object != null ? object.getClass().getCanonicalName() : "null") + "\" must be assignable to \"" + + getSchemaClass().getCanonicalName() + + "\" or be a Json string representation of it for Avro unmarshalling"; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + } + + private boolean isObjectString(final Object object) { + return object != null && avroSchema.getType().equals(Schema.Type.STRING); + } + + @Override + public String marshal2String(final Object object) { + // Condition the object for Avro encoding + final Object conditionedObject = avroObjectMapper.mapToAvro(object); + + final String jsonString = getJsonString(object, conditionedObject); + + return AvroSchemaKeyTranslationUtilities.translateIllegalKeys(jsonString, true); + } + + private String getJsonString(final Object object, final Object conditionedObject) { + + try (final ByteArrayOutputStream output = new ByteArrayOutputStream();) { + final DatumWriter<Object> writer = new GenericDatumWriter<>(avroSchema); + final JsonEncoder jsonEncoder = EncoderFactory.get().jsonEncoder(avroSchema, output, true); + writer.write(conditionedObject, jsonEncoder); + jsonEncoder.flush(); + return new String(output.toByteArray()); + } catch (final Exception e) { + final String returnString = + getUserKey().getID() + ": object \"" + object + "\" Avro marshalling failed: " + e.getMessage(); + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString, e); + } + } + + @Override + public JsonElement marshal2Object(final Object schemaObject) { + // Get the object as a Json string + final String schemaObjectAsString = marshal2String(schemaObject); + + // Get a Gson instance to convert the Json string to an object created by Json + final Gson gson = new Gson(); + + // Convert the Json string into an object + final Object schemaObjectAsObject = gson.fromJson(schemaObjectAsString, Object.class); + + return gson.toJsonTree(schemaObjectAsObject); + } + + /** + * Check if we can pass this object straight through encoding or decoding, is it an object + * native to the schema. + * + * @param object the object to check + * @return true if it's a straight pass through + */ + private boolean passThroughObject(final Object object) { + if (object == null || getSchemaClass() == null) { + return false; + } + + // All strings must be mapped + if (object instanceof String) { + return false; + } + + // Now, check if the object is native + return getSchemaClass().isAssignableFrom(object.getClass()); + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelperParameters.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelperParameters.java new file mode 100644 index 000000000..5a8fac404 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaHelperParameters.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.onap.policy.apex.context.parameters.SchemaHelperParameters; + +/** + * Schema helper parameter class for the Avro schema helper. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroSchemaHelperParameters extends SchemaHelperParameters { + /** + * The Default Constructor sets the {@link AvroSchemaHelper} as the schema helper class for Avro + * schemas. + */ + public AvroSchemaHelperParameters() { + this.setSchemaHelperPluginClass(AvroSchemaHelper.class.getCanonicalName()); + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaKeyTranslationUtilities.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaKeyTranslationUtilities.java new file mode 100644 index 000000000..dc3770a43 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroSchemaKeyTranslationUtilities.java @@ -0,0 +1,140 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import java.util.Map.Entry; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * This static final class contains utility methods for Avro schemas. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public final class AvroSchemaKeyTranslationUtilities { + // Constants for key replacements + private static final String DOT_STRING = "\\."; + private static final String DOT_STRING_REPLACEMENT = "_DoT_"; + private static final String DASH_STRING = "-"; + private static final String DASH_STRING_REPLACEMENT = "_DasH_"; + + /** + * Default constructor to avoid subclassing. + */ + private AvroSchemaKeyTranslationUtilities() {} + + /** + * Translate characters in JSON keys to values that are legal in Avro. Avro names must start + * with [A-Za-z_] and subsequently contain only [A-Za-z0-9_] + * + * @param jsonString The JSON string to translate + * @param revert True if we want to revert the field names to their original values + * @return the translated JSON string + */ + public static String translateIllegalKeys(final String jsonString, final boolean revert) { + if (jsonString == null) { + return jsonString; + } + + // Create a JSON element for the incoming JSON string + final JsonElement jsonElement = + new GsonBuilder().serializeNulls().create().fromJson(jsonString, JsonElement.class); + + final JsonElement translatedJsonElement = translateIllegalKeys(jsonElement, revert); + + return new GsonBuilder().serializeNulls().create().toJson(translatedJsonElement); + } + + /** + * Translate characters in JSON keys to values that are legal in Avro. Avro names must start + * with [A-Za-z_] and subsequently contain only [A-Za-z0-9_] + * + * @param jsonElement The JSON element to translate + * @param revert True if we want to revert the field names to their original values + * @return the translated JSON element + */ + public static JsonElement translateIllegalKeys(final JsonElement jsonElement, final boolean revert) { + // We only act on JSON objects and arrays + if (jsonElement.isJsonObject()) { + return translateIllegalKeys(jsonElement.getAsJsonObject(), revert); + } else if (jsonElement.isJsonArray()) { + return translateIllegalKeys(jsonElement.getAsJsonArray(), revert); + } else { + return jsonElement; + } + } + + /** + * Translate characters in JSON keys to values that are legal in Avro. Avro names must start + * with [A-Za-z_] and subsequently contain only [A-Za-z0-9_] + * + * @param jsonObject The JSON object to translate + * @param revert True if we want to revert the field names to their original values + * @return the translated JSON element + */ + public static JsonElement translateIllegalKeys(final JsonObject jsonObject, final boolean revert) { + final JsonObject newJsonObject = new JsonObject(); + + for (final Entry<String, JsonElement> jsonObjectEntry : jsonObject.entrySet()) { + newJsonObject.add(translateIllegalKey(jsonObjectEntry.getKey(), revert), + translateIllegalKeys(jsonObjectEntry.getValue(), revert)); + } + + return newJsonObject; + } + + /** + * Translate characters in JSON keys to values that are legal in Avro. Avro names must start + * with [A-Za-z_] and subsequently contain only [A-Za-z0-9_] + * + * @param jsonArray The JSON array to translate + * @param revert True if we want to revert the field names to their original values + * @return the translated JSON element + */ + public static JsonElement translateIllegalKeys(final JsonArray jsonArray, final boolean revert) { + final JsonArray newJsonArray = new JsonArray(); + + for (int i = 0; i < jsonArray.size(); i++) { + newJsonArray.add(translateIllegalKeys(jsonArray.get(i), revert)); + } + + return newJsonArray; + } + + /** + * Translate characters in a single JSON key to values that are legal in Avro. Avro names must + * start with [A-Za-z_] and subsequently contain only [A-Za-z0-9_] + * + * @param key The key to translate + * @param revert True if we want to revert the field names to their original values + * @return the translated key + */ + private static String translateIllegalKey(final String key, final boolean revert) { + if (revert) { + return key.replaceAll(DOT_STRING_REPLACEMENT, DOT_STRING).replaceAll(DASH_STRING_REPLACEMENT, DASH_STRING); + } else { + return key.replaceAll(DOT_STRING, DOT_STRING_REPLACEMENT).replaceAll(DASH_STRING, DASH_STRING_REPLACEMENT); + } + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroStringObjectMapper.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroStringObjectMapper.java new file mode 100644 index 000000000..cb3625f31 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/AvroStringObjectMapper.java @@ -0,0 +1,130 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.context.schema.avro; + +import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; +import org.apache.avro.util.Utf8; +import org.onap.policy.apex.context.ContextRuntimeException; +import org.onap.policy.apex.model.basicmodel.concepts.AxKey; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class does string mapping from the Avro Utf8 class to the Java String class. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class AvroStringObjectMapper implements AvroObjectMapper { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroStringObjectMapper.class); + + // The user keyAvro type for direct mapping + private AxKey userKey; + private Type avroType; + + // The Apex compatible class + private final Class<String> schemaClass = String.class; + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getJavaClass() + */ + @Override + public Class<?> getJavaClass() { + return schemaClass; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#setAvroType(org.apache. + * avro. Schema.Type) + */ + @Override + public void init(final AxKey initUserKey, final Type initAvroType) { + this.userKey = initUserKey; + this.avroType = initAvroType; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#createNewinstance(org. + * apache. avro.Schema) + */ + @Override + public Object createNewInstance(final Schema avroSchema) { + // By default, we do not create an instance, normal Java object creation for strings is + // sufficient + return null; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#getAvroType() + */ + @Override + public Type getAvroType() { + return avroType; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapFromAvro(java.lang. + * Object) + */ + @Override + public Object mapFromAvro(final Object avroObject) { + // The Avro object should be a Utf8 object + if (!(avroObject instanceof Utf8)) { + final String returnString = + userKey.getID() + ": object \"" + avroObject + "\" of class \"" + avroObject.getClass() + + "\" cannot be decoded to an object of class \"" + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + return avroObject.toString(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.plugins.context.schema.avro.AvroObjectMapper#mapToAvro(java.lang.Object) + */ + @Override + public Object mapToAvro(final Object object) { + if (object == null) { + final String returnString = userKey.getID() + ": cannot encode a null object of class \"" + + schemaClass.getCanonicalName() + "\""; + LOGGER.warn(returnString); + throw new ContextRuntimeException(returnString); + } + + return object; + } +} diff --git a/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/package-info.java b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/package-info.java new file mode 100644 index 000000000..93f4943d5 --- /dev/null +++ b/plugins/plugins-context/plugins-context-schema/plugins-context-schema-avro/src/main/java/org/onap/policy/apex/plugins/context/schema/avro/package-info.java @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +/** + * Implements <a href="https://avro.apache.org/">Avro</a> schem handling for use in event fields and + * context albums in APEX. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.plugins.context.schema.avro; |