Merge "Add period after inheritDoc for Sonar"
[policy/apex-pdp.git] / context / context-management / src / main / java / org / onap / policy / apex / context / impl / schema / java / JavaSchemaHelper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.apex.context.impl.schema.java;
22
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonElement;
26
27 import java.lang.reflect.Constructor;
28 import java.util.HashMap;
29 import java.util.Map;
30
31 import org.onap.policy.apex.context.ContextRuntimeException;
32 import org.onap.policy.apex.context.impl.schema.AbstractSchemaHelper;
33 import org.onap.policy.apex.context.parameters.ContextParameterConstants;
34 import org.onap.policy.apex.context.parameters.SchemaParameters;
35 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
36 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
37 import org.onap.policy.apex.model.utilities.typeutils.TypeBuilder;
38 import org.onap.policy.common.parameters.ParameterService;
39 import org.slf4j.ext.XLogger;
40 import org.slf4j.ext.XLoggerFactory;
41
42 /**
43  * This class implements translation to and from Apex distributed objects and Java objects when a Java schema is used.
44  * It creates schema items as Java objects and marshals and unmarshals these objects in various formats. All objects
45  * must be of the type of Java class defined in the schema.
46  *
47  * @author Liam Fallon (liam.fallon@ericsson.com)
48  */
49 public class JavaSchemaHelper extends AbstractSchemaHelper {
50     // Get a reference to the logger
51     private static final XLogger LOGGER = XLoggerFactory.getXLogger(JavaSchemaHelper.class);
52
53     // This map defines the built in types in types in Java
54     // @formatter:off
55     private static final Map<String, Class<?>> BUILT_IN_MAP = new HashMap<>();
56
57     static {
58         BUILT_IN_MAP.put("int",    Integer  .TYPE);
59         BUILT_IN_MAP.put("long",   Long     .TYPE);
60         BUILT_IN_MAP.put("double", Double   .TYPE);
61         BUILT_IN_MAP.put("float",  Float    .TYPE);
62         BUILT_IN_MAP.put("bool",   Boolean  .TYPE);
63         BUILT_IN_MAP.put("char",   Character.TYPE);
64         BUILT_IN_MAP.put("byte",   Byte     .TYPE);
65         BUILT_IN_MAP.put("void",   Void     .TYPE);
66         BUILT_IN_MAP.put("short",  Short    .TYPE);
67     }
68     // @formatter:on
69
70     /*
71      * (non-Javadoc)
72      *
73      * @see org.onap.policy.apex.context.impl.schema.AbstractSchemaHelper#init(org.onap.policy.apex.model .basicmodel.
74      * concepts. AxKey, org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema)
75      */
76     @Override
77     public void init(final AxKey userKey, final AxContextSchema schema) {
78         super.init(userKey, schema);
79
80         final String javatype = schema.getSchema();
81         // For Java, the schema is the Java class canonical path
82
83         try {
84             setSchemaClass(TypeBuilder.getJavaTypeClass(schema.getSchema()));
85         } catch (final IllegalArgumentException e) {
86
87             String resultSting = userKey.getId() + ": class/type " + schema.getSchema() + " for context schema \""
88                             + schema.getId() + "\" not found.";
89             if (JavaSchemaHelper.BUILT_IN_MAP.get(javatype) != null) {
90                 resultSting += " Primitive types are not supported. Use the appropriate Java boxing type instead.";
91             } else {
92                 resultSting += " Check the class path of the JVM";
93             }
94             LOGGER.warn(resultSting);
95             throw new ContextRuntimeException(resultSting, e);
96         }
97     }
98
99     /*
100      * (non-Javadoc)
101      *
102      * @see org.onap.policy.apex.context.SchemaHelper#createNewInstance(java.lang.Object)
103      */
104     @Override
105     public Object createNewInstance(final Object incomingObject) {
106         if (incomingObject == null) {
107             return null;
108         }
109
110         if (getSchemaClass() == null) {
111             final String returnString = getUserKey().getId()
112                             + ": could not create an instance, schema class for the schema is null";
113             LOGGER.warn(returnString);
114             throw new ContextRuntimeException(returnString);
115         }
116
117         if (incomingObject instanceof JsonElement) {
118             final String elementJsonString = getGson().toJson((JsonElement) incomingObject);
119             return getGson().fromJson(elementJsonString, this.getSchemaClass());
120         }
121
122         if (getSchemaClass().isAssignableFrom(incomingObject.getClass())) {
123             return incomingObject;
124         }
125
126         final String returnString = getUserKey().getId() + ": the object \"" + incomingObject + "\" of type \""
127                         + incomingObject.getClass().getCanonicalName()
128                         + "\" is not an instance of JsonObject and is not assignable to \""
129                         + getSchemaClass().getCanonicalName() + "\"";
130         LOGGER.warn(returnString);
131         throw new ContextRuntimeException(returnString);
132     }
133
134     /*
135      * (non-Javadoc)
136      *
137      * @see org.onap.policy.apex.context.SchemaHelper#object2SchemaObject(java.lang.Object)
138      */
139     @Override
140     public Object unmarshal(final Object object) {
141         if (object == null) {
142             return null;
143         }
144
145         // If the object is an instance of the incoming object, carry on
146         if (object.getClass().equals(getSchemaClass())) {
147             return object;
148         }
149
150         // For numeric types, do a numeric conversion
151         if (Number.class.isAssignableFrom(getSchemaClass())) {
152             return numericConversion(object);
153         }
154
155         if (getSchemaClass().isAssignableFrom(object.getClass())) {
156             return object;
157         } else {
158             return stringConversion(object);
159         }
160     }
161
162     /*
163      * (non-Javadoc)
164      *
165      * @see org.onap.policy.apex.context.SchemaHelper#schemaObject2Json(java.lang.Object)
166      */
167     @Override
168     public String marshal2String(final Object schemaObject) {
169         if (schemaObject == null) {
170             return "null";
171         }
172
173         // Check the incoming object is of a correct class
174         if (getSchemaClass().isAssignableFrom(schemaObject.getClass())) {
175             // Use Gson to translate the object
176             return getGson().toJson(schemaObject);
177         } else {
178             final String returnString = getUserKey().getId() + ": object \"" + schemaObject.toString()
179                             + "\" of class \"" + schemaObject.getClass().getCanonicalName()
180                             + "\" not compatible with class \"" + getSchemaClass().getCanonicalName() + "\"";
181             LOGGER.warn(returnString);
182             throw new ContextRuntimeException(returnString);
183         }
184     }
185
186     /*
187      * (non-Javadoc)
188      *
189      * @see org.onap.policy.apex.context.SchemaHelper#marshal2JsonElement(java.lang.Object)
190      */
191     @Override
192     public Object marshal2Object(final Object schemaObject) {
193         // Use Gson to marshal the schema object into a Json element to return
194         return getGson().toJsonTree(schemaObject, getSchemaClass());
195     }
196
197     /**
198      * Do a numeric conversion between numeric types.
199      *
200      * @param object The incoming numeric object
201      * @return The converted object
202      */
203     private Object numericConversion(final Object object) {
204         // Check if the incoming object is a number, if not do a string conversion
205         if (object instanceof Number) {
206             if (getSchemaClass().isAssignableFrom(Byte.class)) {
207                 return ((Number) object).byteValue();
208             } else if (getSchemaClass().isAssignableFrom(Short.class)) {
209                 return ((Number) object).shortValue();
210             } else if (getSchemaClass().isAssignableFrom(Integer.class)) {
211                 return ((Number) object).intValue();
212             } else if (getSchemaClass().isAssignableFrom(Long.class)) {
213                 return ((Number) object).longValue();
214             } else if (getSchemaClass().isAssignableFrom(Float.class)) {
215                 return ((Number) object).floatValue();
216             } else if (getSchemaClass().isAssignableFrom(Double.class)) {
217                 return ((Number) object).doubleValue();
218             }
219         }
220
221         // OK, we'll try and convert from a string representation of the incoming object
222         return stringConversion(object);
223     }
224
225     /**
226      * Do a string conversion to the class type.
227      *
228      * @param object The incoming numeric object
229      * @return The converted object
230      */
231     private Object stringConversion(final Object object) {
232         // OK, we'll try and convert from a string representation of the incoming object
233         try {
234             final Constructor<?> stringConstructor = getSchemaClass().getConstructor(String.class);
235             return stringConstructor.newInstance(object.toString());
236         } catch (final Exception e) {
237             final String returnString = getUserKey().getId() + ": object \"" + object.toString() + "\" of class \""
238                             + object.getClass().getCanonicalName() + "\" not compatible with class \""
239                             + getSchemaClass().getCanonicalName() + "\"";
240             LOGGER.warn(returnString, e);
241             throw new ContextRuntimeException(returnString);
242         }
243     }
244
245     /**
246      * Get a GSON instance that has the correct adaptation included.
247      * 
248      * @return the GSON instance
249      */
250     private Gson getGson() {
251         GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting();
252
253         // Get the Java schema helper parameters from the parameter service
254         SchemaParameters schemaParameters = ParameterService.get(ContextParameterConstants.SCHEMA_GROUP_NAME);
255
256         JavaSchemaHelperParameters javaSchemaHelperParmeters = (JavaSchemaHelperParameters) schemaParameters
257                         .getSchemaHelperParameterMap().get("Java");
258         
259         if (javaSchemaHelperParmeters == null) {
260             javaSchemaHelperParmeters = new JavaSchemaHelperParameters();
261         }
262         
263         for (JavaSchemaHelperJsonAdapterParameters jsonAdapterEntry : javaSchemaHelperParmeters.getJsonAdapters()
264                         .values()) {
265
266             Object adapterObject;
267             try {
268                 adapterObject = jsonAdapterEntry.getAdaptorClazz().newInstance();
269             } catch (InstantiationException | IllegalAccessException e) {
270                 final String returnString = getUserKey().getId() + ": instantiation of adapter class \""
271                                 + jsonAdapterEntry.getAdaptorClass() + "\"  to decode and encode class \""
272                                 + jsonAdapterEntry.getAdaptedClass() + "\" failed: " + e.getMessage();
273                 LOGGER.warn(returnString, e);
274                 throw new ContextRuntimeException(returnString);
275             }
276
277             gsonBuilder.registerTypeAdapter(jsonAdapterEntry.getAdaptedClazz(), adapterObject);
278         }
279
280         return gsonBuilder.create();
281     }
282 }