814bfd7450da404d746ebb2454bd0ab9a6cc3570
[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 java.io.IOException;
25 import javax.annotation.Nullable;
26
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
28 import org.opendaylight.yangtools.concepts.Builder;
29 import org.opendaylight.yangtools.yang.binding.DataObject;
30 import org.osgi.framework.Bundle;
31 import org.osgi.framework.BundleContext;
32 import org.osgi.framework.FrameworkUtil;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import com.fasterxml.jackson.annotation.JsonInclude.Include;
37 import com.fasterxml.jackson.core.JsonGenerator;
38 import com.fasterxml.jackson.core.JsonProcessingException;
39 import com.fasterxml.jackson.databind.DeserializationFeature;
40 import com.fasterxml.jackson.databind.ObjectMapper;
41 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
42 import com.fasterxml.jackson.databind.SerializerProvider;
43 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
44 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder.Value;
45 import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
46 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
47 import com.fasterxml.jackson.databind.module.SimpleModule;
48 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
49
50 import org.eclipse.jdt.annotation.NonNull;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
52
53 /**
54  * YangToolsMapper is a specific Jackson mapper configuration for opendaylight yangtools serialization or
55  * deserialization of DataObject to/from JSON TODO ChoiceIn and Credentials deserialization only for
56  * LoginPasswordBuilder
57  */
58 public class YangToolsMapper2<T extends DataObject> extends ObjectMapper {
59
60     private final Logger LOG = LoggerFactory.getLogger(YangToolsMapper2.class);
61     private static final long serialVersionUID = 1L;
62     private static String BUILDER = "Builder";
63
64     private @Nullable final Class<T> clazz;
65     private @Nullable final Class<? extends Builder<? extends T>> builderClazz;
66
67     private BundleContext context;
68
69     /**
70      * Generic Object creation of yangtools java class builder pattern.
71      * 
72      * @param <X> Class of DataObject
73      * @param <B> Builder for the class.
74      * @param clazz specifies class to be mapped
75      * @param builderClazz is the builder for class with name pattern "clazzBuilder".<br>
76      *        If null the clazz is expected to support normal jackson build pattern.
77      * @throws ClassNotFoundException if builderClazz not available in bundle
78      */
79     public <X extends T, B extends Builder<X>> YangToolsMapper2(@NonNull Class<T> clazz,
80             @Nullable Class<B> builderClazz) throws ClassNotFoundException {
81         super();
82         configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
83         setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
84         setSerializationInclusion(Include.NON_NULL);
85         setAnnotationIntrospector(new YangToolsBuilderAnnotationIntrospector());
86         SimpleModule dateAndTimeSerializerModule = new SimpleModule();
87         dateAndTimeSerializerModule.addSerializer(DateAndTime.class, new CustomDateAndTimeSerializer());
88         registerModule(dateAndTimeSerializerModule);
89         Bundle bundle = FrameworkUtil.getBundle(YangToolsMapper2.class);
90
91         this.clazz = clazz;
92         this.builderClazz = builderClazz != null ? builderClazz : getBuilderClass(getBuilderClassName(clazz));
93         context = bundle != null ? bundle.getBundleContext() : null;
94     }
95
96     @Override
97     public String writeValueAsString(Object value) throws JsonProcessingException {
98         return super.writeValueAsString(value);
99     }
100
101     /**
102      * Get Builder object for yang tools interface.
103      * 
104      * @param <T> yang-tools base datatype
105      * @param clazz class with interface.
106      * @return builder for interface or null if not existing
107      */
108     public @Nullable Builder<? extends T> getBuilder(Class<T> clazz) {
109         try {
110             if (builderClazz != null)
111                 return (Builder<? extends T>) builderClazz.newInstance();
112             else
113                 return null;
114         } catch (InstantiationException | IllegalAccessException e) {
115             LOG.debug("Problem ", e);
116             return null;
117         }
118     }
119
120     /**
121      * Callback for handling mapping failures.
122      * 
123      * @return
124      */
125     public int getMappingFailures() {
126         return 0;
127     }
128
129     /**
130      * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
131      * 
132      * @param name with attribute name, not null or empty
133      * @return converted string or null if name was empty or null
134      */
135     public @Nullable static String toCamelCaseAttributeName(final String name) {
136         if (name == null || name.isEmpty())
137             return null;
138
139         final StringBuilder ret = new StringBuilder(name.length());
140         if (!name.startsWith("_"))
141             ret.append('_');
142         int start = 0;
143         for (final String word : name.split("-")) {
144             if (!word.isEmpty()) {
145                 if (start++ == 0) {
146                     ret.append(Character.toLowerCase(word.charAt(0)));
147                 } else {
148                     ret.append(Character.toUpperCase(word.charAt(0)));
149                 }
150                 ret.append(word.substring(1));
151             }
152         }
153         return ret.toString();
154     }
155
156     /**
157      * Verify if builder is available
158      * 
159      * @throws ClassNotFoundException
160      **/
161     public Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
162         return getBuilderClass(getBuilderClassName(clazz));
163     }
164
165     // --- Private functions
166
167     /**
168      * Create name of builder class
169      * 
170      * @param <T>
171      * @param clazz
172      * @return builders class name
173      * @throws ClassNotFoundException
174      */
175     private static String getBuilderClassName(Class<?> clazz) {
176         return clazz.getName() + BUILDER;
177     }
178
179     /**
180      * Search builder in context
181      * 
182      * @param name
183      * @return
184      * @throws ClassNotFoundException
185      */
186     @SuppressWarnings("unchecked")
187     private <X extends T, B extends Builder<X>> Class<B> getBuilderClass(String name) throws ClassNotFoundException {
188         // Try to find in other bundles
189         if (context != null) {
190             //OSGi environment
191             for (Bundle b : context.getBundles()) {
192                 try {
193                     return (Class<B>) b.loadClass(name);
194                 } catch (ClassNotFoundException e) {
195                     // No problem, this bundle doesn't have the class
196                 }
197             }
198             throw new ClassNotFoundException("Can not find Class in OSGi context.");
199         } else {
200             return (Class<B>) Class.forName(name);
201         }
202         // not found in any bundle
203     }
204
205     // --- Classes
206
207     /**
208      * Adapted Builder callbacks
209      */
210     private class YangToolsBuilderAnnotationIntrospector extends JacksonAnnotationIntrospector {
211         private static final long serialVersionUID = 1L;
212
213         @Override
214         public Class<?> findPOJOBuilder(AnnotatedClass ac) {
215
216             if (ac.getRawType().equals(Credentials.class)) {
217                 return org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder.class;
218
219             } else if (ac.getRawType().equals(DateAndTime.class)) {
220                 return DateAndTimeBuilder.class;
221
222             } else if (ac.getRawType().equals(clazz)) {
223                 return builderClazz;
224             }
225
226             if (ac.getRawType().isInterface()) {
227                 String builder = getBuilderClassName(ac.getRawType());
228                 try {
229                     Class<?> innerBuilder = getBuilderClass(builder);
230                     return innerBuilder;
231                 } catch (ClassNotFoundException e) {
232                     // No problem .. try next
233                 }
234             }
235             return super.findPOJOBuilder(ac);
236         }
237
238         @Override
239         public Value findPOJOBuilderConfig(AnnotatedClass ac) {
240             if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
241                 return super.findPOJOBuilderConfig(ac);
242             }
243             return new JsonPOJOBuilder.Value("build", "set");
244         }
245     }
246
247     public static class DateAndTimeBuilder {
248
249         private final String _value;
250
251         public DateAndTimeBuilder(String v) {
252             this._value = v;
253         }
254
255         public DateAndTime build() {
256             return new DateAndTime(_value);
257         }
258
259     }
260     public static class CustomDateAndTimeSerializer extends StdSerializer<@NonNull DateAndTime> {
261
262         private static final long serialVersionUID = 1L;
263
264         public CustomDateAndTimeSerializer() {
265             this(null);
266         }
267
268         protected CustomDateAndTimeSerializer(Class<DateAndTime> t) {
269             super(t);
270         }
271
272         @Override
273         public void serialize(DateAndTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
274             gen.writeString(value.getValue());
275         }
276
277     }
278 }