853e62aa38799302fb46319ac2f09577ad4d4ec5
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / dom / serializer / impl / RuntimeGeneratedMappingServiceImpl.xtend
1 package org.opendaylight.controller.sal.binding.dom.serializer.impl
2
3 import org.opendaylight.controller.sal.binding.dom.serializer.impl.TransformerGenerator
4 import javassist.ClassPool
5 import org.opendaylight.yangtools.yang.model.api.SchemaContext
6 import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
7 import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl
8 import java.util.Map
9 import org.opendaylight.yangtools.sal.binding.model.api.Type
10 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
11 import org.opendaylight.yangtools.yang.model.api.SchemaNode
12 import java.util.concurrent.ConcurrentHashMap
13 import org.opendaylight.yangtools.yang.data.api.CompositeNode
14 import org.opendaylight.yangtools.yang.binding.DataObject
15 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
16 import java.util.Map.Entry
17 import java.util.AbstractMap.SimpleEntry
18 import org.opendaylight.yangtools.yang.model.api.SchemaPath
19 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
20 import org.opendaylight.yangtools.yang.binding.DataContainer
21 import java.util.concurrent.ConcurrentMap
22 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
23 import com.google.common.collect.HashMultimap
24 import com.google.common.util.concurrent.SettableFuture
25 import java.util.concurrent.Future
26 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
27 import org.opendaylight.controller.sal.binding.dom.serializer.impl.LazyGeneratedCodecRegistry
28 import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService
29 import org.slf4j.LoggerFactory
30 import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName
31 import org.opendaylight.controller.sal.binding.dom.serializer.api.DataContainerCodec
32 import org.opendaylight.yangtools.binding.generator.util.Types
33 import org.osgi.framework.BundleContext
34 import java.util.Hashtable
35 import org.osgi.framework.ServiceRegistration
36 import org.opendaylight.controller.sal.binding.impl.connect.dom.DeserializationException
37 import java.util.concurrent.Callable
38 import org.opendaylight.yangtools.yang.binding.Augmentation
39 import org.opendaylight.controller.sal.binding.impl.util.YangSchemaUtils
40 import org.opendaylight.controller.sal.binding.dom.serializer.api.AugmentationCodec
41 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
42 import java.util.ArrayList
43 import org.opendaylight.yangtools.yang.data.api.Node
44 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
45 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
46
47 class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, AutoCloseable {
48
49     @Property
50     ClassPool pool;
51
52     private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl);
53
54     @Property
55     extension TransformerGenerator binding;
56
57     @Property
58     extension LazyGeneratedCodecRegistry registry;
59
60     @Property
61     val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
62
63     @Property
64     val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
65
66     @Property
67     val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
68
69     val promisedTypeDefinitions = HashMultimap.<Type, SettableFuture<GeneratedTypeBuilder>>create;
70
71     val promisedSchemas = HashMultimap.<Type, SettableFuture<SchemaNode>>create;
72
73     ServiceRegistration<SchemaServiceListener> listenerRegistration
74
75     override onGlobalContextUpdated(SchemaContext arg0) {
76         recreateBindingContext(arg0);
77         registry.onGlobalContextUpdated(arg0);
78     }
79
80     def recreateBindingContext(SchemaContext schemaContext) {
81         val newBinding = new BindingGeneratorImpl();
82         newBinding.generateTypes(schemaContext);
83
84         for (entry : newBinding.moduleContexts.entrySet) {
85
86             registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
87             binding.pathToType.putAll(entry.value.childNodes)
88             //val module = entry.key;
89             val context = entry.value;
90             updateBindingFor(context.childNodes, schemaContext);
91             updateBindingFor(context.cases, schemaContext);
92
93             val typedefs = context.typedefs;
94             for (typedef : typedefs.entrySet) {
95                 val typeRef = new ReferencedTypeImpl(typedef.value.packageName,typedef.value.name)
96                 binding.typeDefinitions.put(typeRef, typedef.value as GeneratedType);
97                 val schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext,typedef.key);
98                 if(schemaNode != null) {
99                     
100                     binding.typeToSchemaNode.put(typeRef,schemaNode);
101                 } else {
102                     LOG.error("Type definition for {} is not available",typedef.value);
103                 }
104                 
105             }
106             val augmentations = context.augmentations;
107             for (augmentation : augmentations) {
108                 binding.typeToDefinition.put(augmentation, augmentation);
109             }
110
111             binding.typeToAugmentation.putAll(context.typeToAugmentation);
112         }
113     }
114
115     override CompositeNode toDataDom(DataObject data) {
116         toCompositeNodeImpl(data);
117     }
118
119     override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
120         Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
121         
122         try {
123         val key = toDataDom(entry.key)
124         var CompositeNode data;
125         if(Augmentation.isAssignableFrom(entry.key.targetType)) {
126             data = toCompositeNodeImpl(key,entry.value);
127         } else {
128           data = toCompositeNodeImpl(entry.value);
129         }
130         return new SimpleEntry(key, data);
131         
132         } catch (Exception e) {
133             LOG.error("Error during serialization for {}.", entry.key,e);
134             throw e;
135         }
136     }
137
138     private def CompositeNode toCompositeNodeImpl(DataObject object) {
139         val cls = object.implementedInterface;
140         waitForSchema(cls);
141         val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
142         val ret = codec.serialize(new ValueWithQName(null, object));
143         return ret as CompositeNode;
144     }
145     
146     
147     private def CompositeNode toCompositeNodeImpl(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,DataObject object) {
148        
149         //val cls = object.implementedInterface;
150         //waitForSchema(cls);
151         val last = identifier.path.last;
152         val codec = registry.getCodecForAugmentation(object.implementedInterface as Class) as AugmentationCodec;
153         val ret = codec.serialize(new ValueWithQName(last.nodeType, object));
154         if(last instanceof NodeIdentifierWithPredicates) {
155             val predicates = last as NodeIdentifierWithPredicates;
156             val newNodes = new ArrayList<Node<?>>(predicates.keyValues.size);
157             for(predicate : predicates.keyValues.entrySet) {
158                 newNodes.add(new SimpleNodeTOImpl(predicate.key,null,predicate.value));
159             }
160             newNodes.addAll(ret.children);
161             return new CompositeNodeTOImpl(last.nodeType,null,newNodes);
162         }
163         return ret as CompositeNode;
164     }
165
166     private def void waitForSchema(Class<? extends DataContainer> class1) {
167         if(Augmentation.isAssignableFrom(class1)) {
168             /*  FIXME: We should wait also for augmentations. Currently YANGTools does not provide correct
169              *  mapping between java Augmentation classes and augmentations.
170              */
171             return;
172         }
173         if(registry.isCodecAvailable(class1)) {
174             return;
175         }
176         val ref = Types.typeForClass(class1);
177         getSchemaWithRetry(ref);
178     }
179
180     override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
181         InstanceIdentifier<? extends DataObject> path) {
182         for (arg : path.path) {
183             waitForSchema(arg.type);
184         }
185         return registry.instanceIdentifierCodec.serialize(path);
186     }
187
188     override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
189         return tryDeserialization[ |
190             if (node == null) {
191                 return null;
192             }
193             val targetType = path.targetType
194             val transformer = registry.getCodecForDataObject(targetType);
195             val ret = transformer.deserialize(node)?.value as DataObject;
196             return ret;
197         ]
198     }
199
200     override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) {
201         return tryDeserialization[ |
202             registry.instanceIdentifierCodec.deserialize(entry);
203         ]
204     }
205
206     private static def <T> T tryDeserialization(Callable<T> deserializationBlock) throws DeserializationException {
207         try {
208             deserializationBlock.call()
209         } catch (Exception e) {
210
211             // FIXME: Make this block providing more information.
212             throw new DeserializationException(e);
213         }
214     }
215
216     private def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
217         
218         for (entry : map.entrySet) {
219             val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
220
221             //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
222             val typeRef = new ReferencedTypeImpl(entry.value.packageName,entry.value.name)
223             typeToDefinition.put(typeRef, entry.value);
224             if (schemaNode != null) {
225                 typeToSchemaNode.put(typeRef, schemaNode);
226                 updatePromisedSchemas(typeRef, schemaNode);
227             }
228             
229         }
230     }
231
232     public def void start(BundleContext ctx) {
233         binding = new TransformerGenerator(pool);
234         registry = new LazyGeneratedCodecRegistry()
235         registry.generator = binding
236
237         //binding.staticFieldsInitializer = registry
238         binding.listener = registry
239         binding.typeToDefinition = typeToDefinition
240         binding.typeToSchemaNode = typeToSchemaNode
241         binding.typeDefinitions = typeDefinitions
242         if (ctx !== null) {
243             listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable<String, String>());
244         }
245     }
246
247     private def getSchemaWithRetry(Type type) {
248         val typeDef = typeToSchemaNode.get(type);
249         if (typeDef !== null) {
250             return typeDef;
251         }
252         LOG.info("Thread blocked waiting for schema for: {}",type.fullyQualifiedName)
253         return type.getSchemaInFuture.get();
254     }
255
256     private def Future<SchemaNode> getSchemaInFuture(Type type) {
257         val future = SettableFuture.<SchemaNode>create()
258         promisedSchemas.put(type, future);
259         return future;
260     }
261
262     private def void updatePromisedSchemas(Type builder, SchemaNode schema) {
263         val ref = new ReferencedTypeImpl(builder.packageName, builder.name);
264         val futures = promisedSchemas.get(ref);
265         if (futures === null || futures.empty) {
266             return;
267         }
268         for (future : futures) {
269             future.set(schema);
270         }
271         promisedSchemas.removeAll(builder);
272     }
273
274     override close() throws Exception {
275         listenerRegistration?.unregister();
276     }
277
278 }