Add ability to get yang sources from SchemaContext.
[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 com.google.common.base.Optional
11 import com.google.common.collect.FluentIterable
12 import com.google.common.collect.HashMultimap
13 import com.google.common.util.concurrent.SettableFuture
14 import java.util.AbstractMap.SimpleEntry
15 import java.util.ArrayList
16 import java.util.Collections
17 import java.util.Map
18 import java.util.Map.Entry
19 import java.util.Set
20 import java.util.concurrent.Callable
21 import java.util.concurrent.ConcurrentHashMap
22 import java.util.concurrent.ConcurrentMap
23 import java.util.concurrent.Future
24 import javassist.ClassPool
25 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
26 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
27 import org.opendaylight.yangtools.binding.generator.util.Types
28 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy
29 import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils
30 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
31 import org.opendaylight.yangtools.sal.binding.model.api.Type
32 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
33 import org.opendaylight.yangtools.yang.binding.Augmentation
34 import org.opendaylight.yangtools.yang.binding.BindingMapping
35 import org.opendaylight.yangtools.yang.binding.DataContainer
36 import org.opendaylight.yangtools.yang.binding.DataObject
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
38 import org.opendaylight.yangtools.yang.binding.RpcService
39 import org.opendaylight.yangtools.yang.common.QName
40 import org.opendaylight.yangtools.yang.data.api.CompositeNode
41 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
42 import org.opendaylight.yangtools.yang.data.api.Node
43 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
44 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
45 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec
46 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService
47 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec
48 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException
49 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName
50 import org.opendaylight.yangtools.yang.model.api.Module
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext
52 import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder
53 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener
54 import org.opendaylight.yangtools.yang.model.api.SchemaNode
55 import org.opendaylight.yangtools.yang.model.api.SchemaPath
56 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
57 import org.slf4j.LoggerFactory
58
59 class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
60 SchemaLock, AutoCloseable, SchemaContextHolder {
61
62     @Property
63     ClassPool pool;
64
65     private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl);
66
67     @Property
68     extension TransformerGenerator binding;
69
70     @Property
71     extension LazyGeneratedCodecRegistry registry;
72
73     @Property
74     val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
75
76     @Property
77     val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
78
79     @Property
80     val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
81
82     @Property
83     val ConcurrentMap<Type, Set<QName>> serviceTypeToRpc = new ConcurrentHashMap();
84
85     val promisedTypes = HashMultimap.<Type, SettableFuture<Type>>create;
86
87     val ClassLoadingStrategy classLoadingStrategy;
88
89     @Property
90     var SchemaContext schemaContext;
91
92     new() {
93         this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy())
94     }
95
96     new(ClassLoadingStrategy strat){
97         classLoadingStrategy = strat
98     }
99
100     //ServiceRegistration<SchemaServiceListener> listenerRegistration
101     override onGlobalContextUpdated(SchemaContext arg0) {
102         schemaContext = arg0
103         recreateBindingContext(arg0);
104         registry.onGlobalContextUpdated(arg0);
105     }
106
107     def recreateBindingContext(SchemaContext schemaContext) {
108         val newBinding = new BindingGeneratorImpl();
109         newBinding.generateTypes(schemaContext);
110
111         for (entry : newBinding.moduleContexts.entrySet) {
112
113             registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
114             binding.pathToType.putAll(entry.value.childNodes)
115             val module = entry.key;
116             val context = entry.value;
117             updateBindingFor(context.childNodes, schemaContext);
118             updateBindingFor(context.cases, schemaContext);
119             val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
120
121             if (!module.rpcs.empty) {
122                 val rpcs = FluentIterable.from(module.rpcs).transform[QName].toSet
123                 val serviceClass = new ReferencedTypeImpl(namespace,
124                     BindingGeneratorUtil.parseToClassName(module.name) + "Service");
125                 serviceTypeToRpc.put(serviceClass, rpcs);
126             }
127
128             val typedefs = context.typedefs;
129             for (typedef : typedefs.entrySet) {
130                 val typeRef = new ReferencedTypeImpl(typedef.value.packageName, typedef.value.name)
131                 binding.typeDefinitions.put(typeRef, typedef.value as GeneratedType);
132                 val schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.key);
133                 if (schemaNode != null) {
134
135                     binding.typeToSchemaNode.put(typeRef, schemaNode);
136                 } else {
137                     LOG.error("Type definition for {} is not available", typedef.value);
138                 }
139
140             }
141             val augmentations = context.augmentations;
142             for (augmentation : augmentations) {
143                 binding.typeToDefinition.put(augmentation, augmentation);
144             }
145             binding.typeToAugmentation.putAll(context.typeToAugmentation);
146             for (augmentation : augmentations) {
147                 updatePromisedSchemas(augmentation);
148             }
149         }
150     }
151
152     override CompositeNode toDataDom(DataObject data) {
153         toCompositeNodeImpl(data);
154     }
155
156     override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
157         Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
158
159         try {
160             val key = toDataDom(entry.key)
161             var CompositeNode data;
162             if (Augmentation.isAssignableFrom(entry.key.targetType)) {
163                 data = toCompositeNodeImplAugument(key, entry.value);
164             } else {
165                 data = toCompositeNodeImpl(key, entry.value);
166             }
167             return new SimpleEntry(key, data);
168
169         } catch (Exception e) {
170             LOG.error("Error during serialization for {}.", entry.key, e);
171             throw e;
172         }
173     }
174
175     private def CompositeNode toCompositeNodeImpl(DataObject object) {
176         val cls = object.implementedInterface;
177         waitForSchema(cls);
178         val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
179         val ret = codec.serialize(new ValueWithQName(null, object));
180         return ret as CompositeNode;
181     }
182
183     private def CompositeNode toCompositeNodeImpl(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,
184         DataObject object) {
185         val last = identifier.path.last;
186         val cls = object.implementedInterface;
187         waitForSchema(cls);
188         val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
189         val ret = codec.serialize(new ValueWithQName(last.nodeType, object));
190         return ret as CompositeNode;
191     }
192
193     private def CompositeNode toCompositeNodeImplAugument(
194         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, DataObject object) {
195
196         //val cls = object.implementedInterface;
197         //waitForSchema(cls);
198         val last = identifier.path.last;
199         val codec = registry.getCodecForAugmentation(object.implementedInterface as Class) as AugmentationCodec;
200         val ret = codec.serialize(new ValueWithQName(last.nodeType, object));
201         if (last instanceof NodeIdentifierWithPredicates) {
202             val predicates = last as NodeIdentifierWithPredicates;
203             val newNodes = new ArrayList<Node<?>>(predicates.keyValues.size);
204             for (predicate : predicates.keyValues.entrySet) {
205                 newNodes.add(new SimpleNodeTOImpl(predicate.key, null, predicate.value));
206             }
207             newNodes.addAll(ret.children);
208             return new CompositeNodeTOImpl(last.nodeType, null, newNodes);
209         }
210         return ret as CompositeNode;
211     }
212
213     override waitForSchema(Class class1) {
214
215         if (registry.isCodecAvailable(class1)) {
216             return;
217         }
218         val ref = Types.typeForClass(class1);
219         getSchemaWithRetry(ref);
220     }
221
222     override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
223         InstanceIdentifier<? extends DataObject> path) {
224         for (arg : path.path) {
225             waitForSchema(arg.type);
226         }
227         return registry.instanceIdentifierCodec.serialize(path);
228     }
229
230     override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
231         dataObjectFromDataDom(path.targetType, node) as DataObject;
232     }
233
234     override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) {
235         return tryDeserialization[ |
236             registry.instanceIdentifierCodec.deserialize(entry);
237         ]
238     }
239
240     override getCodecRegistry() {
241         return getRegistry();
242     }
243
244     private static def <T> T tryDeserialization(Callable<T> deserializationBlock) throws DeserializationException {
245         try {
246             deserializationBlock.call()
247         } catch (Exception e) {
248
249             // FIXME: Make this block providing more information.
250             throw new DeserializationException(e);
251         }
252     }
253
254     private def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
255
256         for (entry : map.entrySet) {
257             val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
258
259             //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
260             val typeRef = new ReferencedTypeImpl(entry.value.packageName, entry.value.name)
261             typeToDefinition.put(typeRef, entry.value);
262             if (schemaNode != null) {
263                 typeToSchemaNode.put(typeRef, schemaNode);
264                 updatePromisedSchemas(entry.value);
265             }
266
267         }
268     }
269
270     public def void init() {
271         binding = new TransformerGenerator(pool);
272         registry = new LazyGeneratedCodecRegistry(this, classLoadingStrategy)
273
274         registry.generator = binding
275
276         //binding.staticFieldsInitializer = registry
277         binding.listener = registry
278         binding.typeToDefinition = typeToDefinition
279         binding.typeToSchemaNode = typeToSchemaNode
280         binding.typeDefinitions = typeDefinitions
281
282     //        if (ctx !== null) {
283     //            listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable<String, String>());
284     //        }
285     }
286
287     override getRpcQNamesFor(Class<? extends RpcService> service) {
288         var serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.package.name, service.simpleName))
289         if (serviceRef == null) {
290             serviceRef = Collections.emptySet()
291         }
292         return serviceRef
293     }
294
295     private def void getSchemaWithRetry(Type type) {
296         if (typeToDefinition.containsKey(type)) {
297             return;
298         }
299         LOG.info("Thread blocked waiting for schema for: {}", type.fullyQualifiedName)
300         type.waitForTypeDefinition.get();
301         LOG.info("Schema for {} became available, thread unblocked", type.fullyQualifiedName)
302     }
303
304     private def Future<Type> waitForTypeDefinition(Type type) {
305         val future = SettableFuture.<Type>create()
306         promisedTypes.put(type, future);
307         return future;
308     }
309
310     private def void updatePromisedSchemas(Type builder) {
311         val ref = new ReferencedTypeImpl(builder.packageName, builder.name);
312         val futures = promisedTypes.get(ref);
313         if (futures === null || futures.empty) {
314             return;
315         }
316         for (future : futures) {
317             future.set(builder);
318         }
319         promisedTypes.removeAll(builder);
320     }
321
322     override close() throws Exception {
323         //listenerRegistration?.unregister();
324     }
325
326     override dataObjectFromDataDom(Class<? extends DataContainer> container, CompositeNode domData) {
327         return tryDeserialization[ |
328             if (domData == null) {
329                 return null;
330             }
331             val transformer = registry.getCodecForDataObject(container);
332             val ret = transformer.deserialize(domData)?.value as DataObject;
333             return ret;
334         ]
335     }
336
337     override getRpcServiceClassFor(String namespace, String revision) {
338         val module = schemaContext?.findModuleByName(namespace.toString, QName.parseRevision(revision));
339         if (module == null) {
340             return Optional.absent();
341         }
342         try {
343             val rpcTypeName = module.rpcServiceType;
344             if (rpcTypeName.present) {
345                 val rpcClass = binding.classLoadingStrategy.loadClass(rpcTypeName.get.fullyQualifiedName);
346                 return Optional.of(rpcClass as Class<? extends RpcService>);
347             }
348         } catch (Exception e) {
349             LOG.debug("RPC class not present for {},{}", namespace, revision, e);
350         }
351         return Optional.absent()
352     }
353
354     def Optional<Type> getRpcServiceType(Module module) {
355         val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
356         if (module.rpcs.empty) {
357             return Optional.<Type>absent();
358         }
359         return Optional.<Type>of(
360             new ReferencedTypeImpl(namespace,
361                 BindingMapping.getClassName(module.name) + BindingMapping.RPC_SERVICE_SUFFIX));
362     }
363 }