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