7eb473ce3401a6774ed31f9690db658f24e4a576
[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
41 class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, AutoCloseable {
42
43     @Property
44     ClassPool pool;
45
46     private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl);
47
48     @Property
49     extension TransformerGenerator binding;
50
51     @Property
52     extension LazyGeneratedCodecRegistry registry;
53
54     @Property
55     val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
56
57     @Property
58     val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
59
60     @Property
61     val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
62
63     val promisedTypeDefinitions = HashMultimap.<Type, SettableFuture<GeneratedTypeBuilder>>create;
64
65     val promisedSchemas = HashMultimap.<Type, SettableFuture<SchemaNode>>create;
66
67     ServiceRegistration<SchemaServiceListener> listenerRegistration
68
69     override onGlobalContextUpdated(SchemaContext arg0) {
70         recreateBindingContext(arg0);
71         registry.onGlobalContextUpdated(arg0);
72     }
73
74     def recreateBindingContext(SchemaContext schemaContext) {
75         val newBinding = new BindingGeneratorImpl();
76         newBinding.generateTypes(schemaContext);
77
78         for (entry : newBinding.moduleContexts.entrySet) {
79
80             registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
81
82             //val module = entry.key;
83             val context = entry.value;
84             updateBindingFor(context.childNodes, schemaContext);
85             updateBindingFor(context.cases, schemaContext);
86
87             val typedefs = context.typedefs;
88             for (typedef : typedefs.entrySet) {
89                 val typeRef = new ReferencedTypeImpl(typedef.value.packageName,typedef.value.name)
90                 binding.typeDefinitions.put(typeRef, typedef.value as GeneratedType);
91                 val schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext,typedef.key);
92                 if(schemaNode != null) {
93                     
94                     binding.typeToSchemaNode.put(typeRef,schemaNode);
95                 } else {
96                     LOG.error("Type definition for {} is not available",typedef.value);
97                 }
98                 
99             }
100             val augmentations = context.augmentations;
101             for (augmentation : augmentations) {
102                 binding.typeToDefinition.put(augmentation, augmentation);
103             }
104
105             binding.typeToAugmentation.putAll(context.typeToAugmentation);
106         }
107     }
108
109     override CompositeNode toDataDom(DataObject data) {
110         toCompositeNodeImpl(data);
111     }
112
113     override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
114         Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
115         val key = toDataDom(entry.key)
116         val data = toCompositeNodeImpl(entry.value);
117         return new SimpleEntry(key, data);
118     }
119
120     private def CompositeNode toCompositeNodeImpl(DataObject object) {
121         val cls = object.implementedInterface;
122         waitForSchema(cls);
123         val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
124         val ret = codec.serialize(new ValueWithQName(null, object));
125         return ret as CompositeNode;
126     }
127
128     private def void waitForSchema(Class<? extends DataContainer> class1) {
129         if(Augmentation.isAssignableFrom(class1)) {
130             /*  FIXME: We should wait also for augmentations. Currently YANGTools does not provide correct
131              *  mapping between java Augmentation classes and augmentations.
132              */
133             return;
134         }
135         
136         val ref = Types.typeForClass(class1);
137         getSchemaWithRetry(ref);
138     }
139
140     override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
141         InstanceIdentifier<? extends DataObject> path) {
142         for (arg : path.path) {
143             waitForSchema(arg.type);
144         }
145         return registry.instanceIdentifierCodec.serialize(path);
146     }
147
148     override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
149         return tryDeserialization[ |
150             if (node == null) {
151                 return null;
152             }
153             val targetType = path.targetType
154             val transformer = registry.getCodecForDataObject(targetType);
155             val ret = transformer.deserialize(node)?.value as DataObject;
156             return ret;
157         ]
158     }
159
160     override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) {
161         return tryDeserialization[ |
162             registry.instanceIdentifierCodec.deserialize(entry);
163         ]
164     }
165
166     private static def <T> T tryDeserialization(Callable<T> deserializationBlock) throws DeserializationException {
167         try {
168             deserializationBlock.call()
169         } catch (Exception e) {
170
171             // FIXME: Make this block providing more information.
172             throw new DeserializationException(e);
173         }
174     }
175
176     private def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
177         for (entry : map.entrySet) {
178             val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
179
180             //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
181             if (schemaNode != null) {
182                 val typeRef = new ReferencedTypeImpl(entry.value.packageName,entry.value.name)
183                 typeToSchemaNode.put(typeRef, schemaNode);
184                 typeToDefinition.put(typeRef, entry.value);
185                 updatePromisedSchemas(typeRef, schemaNode);
186             }
187         }
188     }
189
190     public def void start(BundleContext ctx) {
191         binding = new TransformerGenerator(pool);
192         registry = new LazyGeneratedCodecRegistry()
193         registry.generator = binding
194
195         //binding.staticFieldsInitializer = registry
196         binding.listener = registry
197         binding.typeToDefinition = typeToDefinition
198         binding.typeToSchemaNode = typeToSchemaNode
199         binding.typeDefinitions = typeDefinitions
200         if (ctx !== null) {
201             listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable<String, String>());
202         }
203     }
204
205     private def getTypeDefinition(Type type) {
206         val typeDef = typeToDefinition.get(type);
207         if (typeDef !== null) {
208             return typeDef;
209         }
210         return type.getTypeDefInFuture.get();
211     }
212
213     private def Future<GeneratedTypeBuilder> getTypeDefInFuture(Type type) {
214         val future = SettableFuture.<GeneratedTypeBuilder>create()
215         promisedTypeDefinitions.put(type, future);
216         return future;
217     }
218
219     private def void updatePromisedTypeDefinitions(GeneratedTypeBuilder builder) {
220         val futures = promisedTypeDefinitions.get(builder);
221         if (futures === null || futures.empty) {
222             return;
223         }
224         for (future : futures) {
225             future.set(builder);
226         }
227         promisedTypeDefinitions.removeAll(builder);
228     }
229
230     private def getSchemaWithRetry(Type type) {
231         val typeDef = typeToSchemaNode.get(type);
232         if (typeDef !== null) {
233             return typeDef;
234         }
235         LOG.info("Thread blocked waiting for schema for: {}",type.fullyQualifiedName)
236         return type.getSchemaInFuture.get();
237     }
238
239     private def Future<SchemaNode> getSchemaInFuture(Type type) {
240         val future = SettableFuture.<SchemaNode>create()
241         promisedSchemas.put(type, future);
242         return future;
243     }
244
245     private def void updatePromisedSchemas(Type builder, SchemaNode schema) {
246         val ref = new ReferencedTypeImpl(builder.packageName, builder.name);
247         val futures = promisedSchemas.get(ref);
248         if (futures === null || futures.empty) {
249             return;
250         }
251         for (future : futures) {
252             future.set(schema);
253         }
254         promisedSchemas.removeAll(builder);
255     }
256
257     override close() throws Exception {
258         listenerRegistration?.unregister();
259     }
260
261 }