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