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