8f05437c94be6ad4cf1064f38ba6fdc45e9f453f
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.yangtools;
23
24 import com.fasterxml.jackson.annotation.JsonInclude.Include;
25 import com.fasterxml.jackson.core.JsonGenerator;
26 import com.fasterxml.jackson.core.JsonProcessingException;
27 import com.fasterxml.jackson.databind.DeserializationFeature;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
30 import com.fasterxml.jackson.databind.SerializerProvider;
31 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
32 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder.Value;
33 import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
34 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
35 import com.fasterxml.jackson.databind.module.SimpleModule;
36 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
37 import java.io.IOException;
38 import javax.annotation.Nullable;
39 import org.eclipse.jdt.annotation.NonNull;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.pm.types.rev191129.PmDataType;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
43 import org.opendaylight.yangtools.concepts.Builder;
44 import org.opendaylight.yangtools.yang.binding.DataObject;
45 import org.opendaylight.yangtools.yang.binding.TypeObject;
46 import org.osgi.framework.Bundle;
47 import org.osgi.framework.BundleContext;
48 import org.osgi.framework.FrameworkUtil;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53  * YangToolsMapper is a specific Jackson mapper configuration for opendaylight yangtools serialization or
54  * deserialization of DataObject to/from JSON TODO ChoiceIn and Credentials deserialization only for
55  * LoginPasswordBuilder
56  */
57 public class YangToolsMapper2<T extends DataObject> extends ObjectMapper {
58
59     private static final Logger LOG = LoggerFactory.getLogger(YangToolsMapper2.class);
60     private static final long serialVersionUID = 1L;
61     private static String BUILDER = "Builder";
62
63     private @Nullable final Class<T> clazz;
64     private @Nullable final Class<? extends Builder<? extends T>> builderClazz;
65
66     private BundleContext context;
67
68     /**
69      * Generic Object creation of yangtools java class builder pattern.
70      *
71      * @param <X> Class of DataObject
72      * @param <B> Builder for the class.
73      * @param clazz specifies class to be mapped
74      * @param builderClazz is the builder for class with name pattern "clazzBuilder".<br>
75      *        If null the clazz is expected to support normal jackson build pattern.
76      * @throws ClassNotFoundException if builderClazz not available in bundle
77      */
78     public <X extends T, B extends Builder<X>> YangToolsMapper2(@NonNull Class<T> clazz,
79             @Nullable Class<B> builderClazz) throws ClassNotFoundException {
80         super();
81         configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
82         setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
83         setSerializationInclusion(Include.NON_NULL);
84         setAnnotationIntrospector(new YangToolsBuilderAnnotationIntrospector());
85         SimpleModule dateAndTimeSerializerModule = new SimpleModule();
86         dateAndTimeSerializerModule.addSerializer(DateAndTime.class, new CustomDateAndTimeSerializer());
87         registerModule(dateAndTimeSerializerModule);
88
89         SimpleModule pmDataTypeSerializerModule = new SimpleModule();
90         pmDataTypeSerializerModule.addSerializer(PmDataType.class, new CustomPMDataTypeSerializer());
91         registerModule(pmDataTypeSerializerModule);
92         Bundle bundle = FrameworkUtil.getBundle(YangToolsMapper2.class);
93
94         this.clazz = clazz;
95         this.builderClazz = builderClazz != null ? builderClazz : getBuilderClass(getBuilderClassName(clazz));
96         context = bundle != null ? bundle.getBundleContext() : null;
97     }
98
99     public <X extends T, B extends Builder<X>> YangToolsMapper2(@NonNull Class<T> clazz) throws ClassNotFoundException {
100         this(clazz, null);
101     }
102
103     @Override
104     public String writeValueAsString(Object value) throws JsonProcessingException {
105         return super.writeValueAsString(value);
106     }
107
108     /**
109      * Get Builder object for yang tools interface.
110      *
111      * @param <T> yang-tools base datatype
112      * @param clazz class with interface.
113      * @return builder for interface or null if not existing
114      */
115     public @Nullable Builder<? extends T> getBuilder(Class<T> clazz) {
116         try {
117             if (builderClazz != null)
118                 return builderClazz.newInstance();
119             else
120                 return null;
121         } catch (InstantiationException | IllegalAccessException e) {
122             LOG.debug("Problem ", e);
123             return null;
124         }
125     }
126
127     /**
128      * Callback for handling mapping failures.
129      *
130      * @return
131      */
132     public int getMappingFailures() {
133         return 0;
134     }
135
136     /**
137      * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
138      *
139      * @param name with attribute name, not null or empty
140      * @return converted string or null if name was empty or null
141      */
142     public @Nullable static String toCamelCaseAttributeName(final String name) {
143         if (name == null || name.isEmpty())
144             return null;
145
146         final StringBuilder ret = new StringBuilder(name.length());
147         if (!name.startsWith("_"))
148             ret.append('_');
149         int start = 0;
150         for (final String word : name.split("-")) {
151             if (!word.isEmpty()) {
152                 if (start++ == 0) {
153                     ret.append(Character.toLowerCase(word.charAt(0)));
154                 } else {
155                     ret.append(Character.toUpperCase(word.charAt(0)));
156                 }
157                 ret.append(word.substring(1));
158             }
159         }
160         return ret.toString();
161     }
162
163     /**
164      * Verify if builder is available
165      *
166      * @throws ClassNotFoundException
167      **/
168     public Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
169         return getBuilderClass(getBuilderClassName(clazz));
170     }
171
172     // --- Private functions
173
174     /**
175      * Create name of builder class
176      *
177      * @param <T>
178      * @param clazz
179      * @return builders class name
180      * @throws ClassNotFoundException
181      */
182     private static String getBuilderClassName(Class<?> clazz) {
183         return clazz.getName() + BUILDER;
184     }
185
186     /**
187      * Search builder in context
188      *
189      * @param name
190      * @return
191      * @throws ClassNotFoundException
192      */
193     @SuppressWarnings("unchecked")
194     private <X extends T, B extends Builder<X>> Class<B> getBuilderClass(String name) throws ClassNotFoundException {
195         // Try to find in other bundles
196         if (context != null) {
197             //OSGi environment
198             for (Bundle b : context.getBundles()) {
199                 LOG.info("Search in bundle: {}", b.getSymbolicName());
200                 try {
201                     return (Class<B>) b.loadClass(name);
202                 } catch (ClassNotFoundException e) {
203                     // No problem, this bundle doesn't have the class
204                 }
205             }
206             throw new ClassNotFoundException("Can not find Class in OSGi context.");
207         } else {
208             return (Class<B>) Class.forName(name);
209         }
210         // not found in any bundle
211     }
212
213     // --- Classes
214
215     /**
216      * Adapted Builder callbacks
217      */
218     private class YangToolsBuilderAnnotationIntrospector extends JacksonAnnotationIntrospector {
219         private static final long serialVersionUID = 1L;
220
221         @Override
222         public Class<?> findPOJOBuilder(AnnotatedClass ac) {
223             Class<?> clazz2build = ac.getRawType();
224             if (clazz2build.equals(Credentials.class)) {
225                 return org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder.class;
226
227             } else if (clazz2build.equals(DateAndTime.class)) {
228                 return DateAndTimeBuilder.class;
229                 //            } else if (ac.getRawType().equals(PmDataType.class)) {
230                 //                LOG.info("Builder class");
231                 //                return PMDataTypeBuilder.class;
232             } else if (clazz2build.equals(clazz)) {
233                 return builderClazz;
234             }
235
236             if (clazz2build.isInterface() || TypeObject.class.isAssignableFrom(clazz2build)) {
237                 String builder = getBuilderClassName(clazz2build);
238                 LOG.info("Search: {}", builder);
239                 Class<?> innerBuilder;
240                 try {
241                     innerBuilder = Class.forName(builder);
242                     LOG.info("Found1: {}", innerBuilder);
243                     return innerBuilder;
244                 } catch (ClassNotFoundException e) {
245                     LOG.info("Could not find {}", clazz2build);
246                     // No problem .. try next
247                 }
248                 try {
249                     innerBuilder = getBuilderClass(builder);
250                     LOG.info("Found2: {}", innerBuilder);
251                     return innerBuilder;
252                 } catch (ClassNotFoundException e) {
253                     LOG.info("Could not find {}", clazz2build);
254                     // No problem .. try next
255                 }
256             }
257             Class<?> clazz = super.findPOJOBuilder(ac);
258             return clazz;
259         }
260
261         @Override
262         public Value findPOJOBuilderConfig(AnnotatedClass ac) {
263             if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
264                 return super.findPOJOBuilderConfig(ac);
265             }
266             return new JsonPOJOBuilder.Value("build", "set");
267         }
268     }
269
270     public static class DateAndTimeBuilder {
271
272         private final String _value;
273
274         public DateAndTimeBuilder(String v) {
275             this._value = v;
276         }
277
278         public DateAndTime build() {
279             return new DateAndTime(_value);
280         }
281
282     }
283     public static class CustomDateAndTimeSerializer extends StdSerializer<@NonNull DateAndTime> {
284
285         private static final long serialVersionUID = 1L;
286
287         public CustomDateAndTimeSerializer() {
288             this(null);
289         }
290
291         protected CustomDateAndTimeSerializer(Class<DateAndTime> t) {
292             super(t);
293         }
294
295         @Override
296         public void serialize(DateAndTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
297             gen.writeString(value.getValue());
298         }
299
300     }
301     public static class CustomPMDataTypeSerializer extends StdSerializer<@NonNull PmDataType> {
302
303         private static final long serialVersionUID = 1L;
304
305         public CustomPMDataTypeSerializer() {
306             this(null);
307         }
308
309         protected CustomPMDataTypeSerializer(Class<PmDataType> t) {
310             super(t);
311         }
312
313         @Override
314         public void serialize(PmDataType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
315             gen.writeString(value.toString());
316         }
317
318     }
319 }