2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk features
4 * ================================================================================
5 * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.yangtools;
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;
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
57 public class YangToolsMapper2<T extends DataObject> extends ObjectMapper {
59 private static final Logger LOG = LoggerFactory.getLogger(YangToolsMapper2.class);
60 private static final long serialVersionUID = 1L;
61 private static String BUILDER = "Builder";
63 private @Nullable final Class<T> clazz;
64 private @Nullable final Class<? extends Builder<? extends T>> builderClazz;
66 private BundleContext context;
69 * Generic Object creation of yangtools java class builder pattern.
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
78 public <X extends T, B extends Builder<X>> YangToolsMapper2(@NonNull Class<T> clazz,
79 @Nullable Class<B> builderClazz) throws ClassNotFoundException {
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);
89 SimpleModule pmDataTypeSerializerModule = new SimpleModule();
90 pmDataTypeSerializerModule.addSerializer(PmDataType.class, new CustomPMDataTypeSerializer());
91 registerModule(pmDataTypeSerializerModule);
92 Bundle bundle = FrameworkUtil.getBundle(YangToolsMapper2.class);
95 this.builderClazz = builderClazz != null ? builderClazz : getBuilderClass(getBuilderClassName(clazz));
96 context = bundle != null ? bundle.getBundleContext() : null;
99 public <X extends T, B extends Builder<X>> YangToolsMapper2(@NonNull Class<T> clazz) throws ClassNotFoundException {
104 public String writeValueAsString(Object value) throws JsonProcessingException {
105 return super.writeValueAsString(value);
109 * Get Builder object for yang tools interface.
111 * @param <T> yang-tools base datatype
112 * @param clazz class with interface.
113 * @return builder for interface or null if not existing
115 public @Nullable Builder<? extends T> getBuilder(Class<T> clazz) {
117 if (builderClazz != null)
118 return builderClazz.newInstance();
121 } catch (InstantiationException | IllegalAccessException e) {
122 LOG.debug("Problem ", e);
128 * Callback for handling mapping failures.
132 public int getMappingFailures() {
137 * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
139 * @param name with attribute name, not null or empty
140 * @return converted string or null if name was empty or null
142 public @Nullable static String toCamelCaseAttributeName(final String name) {
143 if (name == null || name.isEmpty())
146 final StringBuilder ret = new StringBuilder(name.length());
147 if (!name.startsWith("_"))
150 for (final String word : name.split("-")) {
151 if (!word.isEmpty()) {
153 ret.append(Character.toLowerCase(word.charAt(0)));
155 ret.append(Character.toUpperCase(word.charAt(0)));
157 ret.append(word.substring(1));
160 return ret.toString();
164 * Verify if builder is available
166 * @throws ClassNotFoundException
168 public Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
169 return getBuilderClass(getBuilderClassName(clazz));
172 // --- Private functions
175 * Create name of builder class
179 * @return builders class name
180 * @throws ClassNotFoundException
182 private static String getBuilderClassName(Class<?> clazz) {
183 return clazz.getName() + BUILDER;
187 * Search builder in context
191 * @throws ClassNotFoundException
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) {
198 for (Bundle b : context.getBundles()) {
199 LOG.info("Search in bundle: {}", b.getSymbolicName());
201 return (Class<B>) b.loadClass(name);
202 } catch (ClassNotFoundException e) {
203 // No problem, this bundle doesn't have the class
206 throw new ClassNotFoundException("Can not find Class in OSGi context.");
208 return (Class<B>) Class.forName(name);
210 // not found in any bundle
216 * Adapted Builder callbacks
218 private class YangToolsBuilderAnnotationIntrospector extends JacksonAnnotationIntrospector {
219 private static final long serialVersionUID = 1L;
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;
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)) {
236 if (clazz2build.isInterface() || TypeObject.class.isAssignableFrom(clazz2build)) {
237 String builder = getBuilderClassName(clazz2build);
238 LOG.info("Search: {}", builder);
239 Class<?> innerBuilder;
241 innerBuilder = Class.forName(builder);
242 LOG.info("Found1: {}", innerBuilder);
244 } catch (ClassNotFoundException e) {
245 LOG.info("Could not find {}", clazz2build);
246 // No problem .. try next
249 innerBuilder = getBuilderClass(builder);
250 LOG.info("Found2: {}", innerBuilder);
252 } catch (ClassNotFoundException e) {
253 LOG.info("Could not find {}", clazz2build);
254 // No problem .. try next
257 Class<?> clazz = super.findPOJOBuilder(ac);
262 public Value findPOJOBuilderConfig(AnnotatedClass ac) {
263 if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
264 return super.findPOJOBuilderConfig(ac);
266 return new JsonPOJOBuilder.Value("build", "set");
270 public static class DateAndTimeBuilder {
272 private final String _value;
274 public DateAndTimeBuilder(String v) {
278 public DateAndTime build() {
279 return new DateAndTime(_value);
283 public static class CustomDateAndTimeSerializer extends StdSerializer<@NonNull DateAndTime> {
285 private static final long serialVersionUID = 1L;
287 public CustomDateAndTimeSerializer() {
291 protected CustomDateAndTimeSerializer(Class<DateAndTime> t) {
296 public void serialize(DateAndTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
297 gen.writeString(value.getValue());
301 public static class CustomPMDataTypeSerializer extends StdSerializer<@NonNull PmDataType> {
303 private static final long serialVersionUID = 1L;
305 public CustomPMDataTypeSerializer() {
309 protected CustomPMDataTypeSerializer(Class<PmDataType> t) {
314 public void serialize(PmDataType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
315 gen.writeString(value.toString());