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