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