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