fba45b22a0958365c2546ea0895a2075e98a9707
[policy/apex-pdp.git] / plugins / plugins-context / plugins-context-schema / plugins-context-schema-avro / src / main / java / org / onap / policy / apex / plugins / context / schema / avro / AvroObjectMapperFactory.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2020 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.plugins.context.schema.avro;
23
24 import java.util.List;
25 import java.util.Map;
26 import java.util.TreeMap;
27
28 import org.apache.avro.Schema;
29 import org.onap.policy.apex.context.ContextRuntimeException;
30 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
31 import org.slf4j.ext.XLogger;
32 import org.slf4j.ext.XLoggerFactory;
33
34 /**
35  * This class maps between Avro types to Java types. This class is thread safe.
36  *
37  * @author Liam Fallon (liam.fallon@ericsson.com)
38  */
39 public class AvroObjectMapperFactory {
40     // Get a reference to the logger
41     private static final XLogger LOGGER = XLoggerFactory.getXLogger(AvroObjectMapperFactory.class);
42
43     // Map for Avro primitive types to Java primitive types
44     private static final Map<Schema.Type, Class<? extends AvroObjectMapper>> AVRO_OBJECT_MAPPER_MAP = new TreeMap<>();
45
46     // @formatter:off
47     // Initialize the mapping
48     static {
49         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.ARRAY, AvroArrayObjectMapper.class);
50         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.BOOLEAN, AvroDirectObjectMapper.class);
51         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.BYTES, AvroBytesObjectMapper.class);
52         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.DOUBLE, AvroDirectObjectMapper.class);
53         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.ENUM, AvroEnumObjectMapper.class);
54         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.FIXED, AvroDirectObjectMapper.class);
55         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.FLOAT, AvroDirectObjectMapper.class);
56         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.INT, AvroDirectObjectMapper.class);
57         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.LONG, AvroDirectObjectMapper.class);
58         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.MAP, AvroDirectObjectMapper.class);
59         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.NULL, AvroDirectObjectMapper.class);
60         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.RECORD, AvroRecordObjectMapper.class);
61         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.STRING, AvroStringObjectMapper.class);
62         AVRO_OBJECT_MAPPER_MAP.put(Schema.Type.UNION, null);
63     }
64     // @formatter:on
65
66     /**
67      * Gets the Avro object mapper to use for an artifact with the given key and schema.
68      *
69      * @param userKey the key of the artifact
70      * @param incomingSchema the incoming schema
71      * @return the avro object mapper
72      */
73     public AvroObjectMapper get(final AxKey userKey, final Schema incomingSchema) {
74         Schema schema = incomingSchema;
75         boolean isnullable = false;
76         if (Schema.Type.UNION.equals(schema.getType())) {
77
78             final List<Schema> types = schema.getTypes();
79
80             // currently only support unions with 2 types, one of which is NULL
81             final Schema nullschema = Schema.create(Schema.Type.NULL);
82             if (types.size() != 2 || !types.contains(nullschema)) {
83                 final String resultSting = userKey.getId()
84                         + ": Apex currently only supports UNION schemas with 2 options, one must be NULL";
85                 LOGGER.warn(resultSting);
86                 throw new ContextRuntimeException(resultSting);
87             }
88             isnullable = true;
89             // get the non-null schema given for the union so it can be wrapped
90             schema = types.get(0);
91             if (Schema.Type.NULL.equals(schema.getType())) {
92                 schema = types.get(1);
93             }
94             if (Schema.Type.NULL.equals(schema.getType())) {
95                 final String resultSting =
96                         userKey.getId() + ": Apex currently only supports UNION schema2 with 2 options, "
97                                 + "only one can be NULL, and the other cannot be another UNION";
98                 LOGGER.warn(resultSting);
99                 throw new ContextRuntimeException(resultSting);
100             }
101         }
102
103         final Schema.Type avroType = schema.getType();
104
105         // Check that there is a definition for the mapper for this type
106         if (!AVRO_OBJECT_MAPPER_MAP.containsKey(avroType) || AVRO_OBJECT_MAPPER_MAP.get(avroType) == null) {
107             final String resultSting =
108                     userKey.getId() + ": no Avro object mapper defined for Avro type \"" + avroType + "\"";
109             LOGGER.warn(resultSting);
110             throw new ContextRuntimeException(resultSting);
111         }
112
113         // Create a mapper
114         AvroObjectMapper avroObjectMapper;
115         try {
116             avroObjectMapper = AVRO_OBJECT_MAPPER_MAP.get(avroType).getDeclaredConstructor().newInstance();
117             if (isnullable) {
118                 avroObjectMapper = new AvroNullableMapper(avroObjectMapper);
119             }
120
121         } catch (final Exception e) {
122             final String resultSting = userKey.getId() + ": could not create an Avro object mapper of type \""
123                     + AVRO_OBJECT_MAPPER_MAP.get(avroType) + "\" for Avro type \"" + avroType + "\" : " + e;
124             LOGGER.warn(resultSting, e);
125             throw new ContextRuntimeException(resultSting, e);
126         }
127
128         // Set the type and return
129         avroObjectMapper.init(userKey, avroType);
130
131         return avroObjectMapper;
132     }
133 }