Merge changes Ibfbb4deb,I96e28324
[mdsal.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / RuntimeGeneratedMappingServiceImpl.java
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 java.net.URI;
11 import java.util.AbstractMap.SimpleEntry;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.ExecutionException;
24
25 import javassist.ClassPool;
26
27 import javax.annotation.concurrent.GuardedBy;
28
29 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
30 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
31 import org.opendaylight.yangtools.binding.generator.util.Types;
32 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
33 import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils;
34 import org.opendaylight.yangtools.sal.binding.model.api.Type;
35 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
36 import org.opendaylight.yangtools.yang.binding.Augmentation;
37 import org.opendaylight.yangtools.yang.binding.BindingMapping;
38 import org.opendaylight.yangtools.yang.binding.DataContainer;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
42 import org.opendaylight.yangtools.yang.binding.RpcService;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
45 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
47 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
48 import org.opendaylight.yangtools.yang.data.api.Node;
49 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
50 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
51 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
52 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
53 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
54 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
55 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
56 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
57 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
58 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
59 import org.opendaylight.yangtools.yang.model.api.Module;
60 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
61 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
62 import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
64 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
66 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
67 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 import com.google.common.base.Optional;
72 import com.google.common.base.Preconditions;
73 import com.google.common.collect.HashMultimap;
74 import com.google.common.collect.Multimap;
75 import com.google.common.util.concurrent.ListenableFuture;
76 import com.google.common.util.concurrent.SettableFuture;
77
78 public class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
79         SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
80
81     private static final Logger LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl.class);
82
83     private final ConcurrentMap<Type, Set<QName>> serviceTypeToRpc = new ConcurrentHashMap<>();
84
85     /**
86      * This is map of types which users are waiting for.
87      */
88     @GuardedBy("this")
89     private final Multimap<Type, SettableFuture<Type>> promisedTypes = HashMultimap.create();
90
91     private final ClassLoadingStrategy classLoadingStrategy;
92
93     private final AbstractTransformerGenerator binding;
94     private final LazyGeneratedCodecRegistry registry;
95     private final ClassPool pool;
96
97     /*
98      * FIXME: updated here, access from AbstractTransformer
99      */
100     private final Map<Type, AugmentationSchema> typeToAugmentation = new ConcurrentHashMap<>();
101     private final ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap<>();
102     private final ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap<>();
103     private final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
104
105     // FIXME: need to figure these out
106     private final ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap<>();
107     private SchemaContext schemaContext;
108
109     public RuntimeGeneratedMappingServiceImpl(final ClassPool pool) {
110         this(pool, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
111     }
112
113     public RuntimeGeneratedMappingServiceImpl(final ClassPool pool, final ClassLoadingStrategy strat) {
114         this.pool = Preconditions.checkNotNull(pool);
115         this.classLoadingStrategy = Preconditions.checkNotNull(strat);
116         // FIXME: this escapes constructor
117         binding = new TransformerGenerator(this, pool);
118         registry = new LazyGeneratedCodecRegistry(this, binding, classLoadingStrategy);
119         binding.setListener(registry);
120
121         // if (ctx !== null) {
122         // listenerRegistration = ctx.registerService(SchemaServiceListener,
123         // this, new Hashtable<String, String>());
124         // }
125     }
126
127     @Override
128     public synchronized SchemaContext getSchemaContext() {
129         return schemaContext;
130     }
131
132     @Override
133     public synchronized void onGlobalContextUpdated(final SchemaContext context) {
134         this.schemaContext = Preconditions.checkNotNull(context);
135         this.recreateBindingContext(context);
136         this.registry.onGlobalContextUpdated(context);
137     }
138
139     @GuardedBy("this")
140     private void recreateBindingContext(final SchemaContext schemaContext) {
141         BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
142         newBinding.generateTypes(schemaContext);
143
144         for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
145
146             registry.onModuleContextAdded(schemaContext, entry.getKey(), entry.getValue());
147             pathToType.putAll(entry.getValue().getChildNodes());
148             Module module = entry.getKey();
149             ModuleContext context = entry.getValue();
150             updateBindingFor(context.getChildNodes(), schemaContext);
151             updateBindingFor(context.getCases(), schemaContext);
152             String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
153
154             if (!module.getRpcs().isEmpty()) {
155                 Set<QName> rpcs = new HashSet<>();
156                 for (RpcDefinition rpc : module.getRpcs()) {
157                     rpcs.add(rpc.getQName());
158                 }
159                 Type serviceClass = new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
160                         + "Service");
161                 serviceTypeToRpc.put(serviceClass, rpcs);
162             }
163
164             Map<SchemaPath, Type> typedefs = context.getTypedefs();
165             for (Map.Entry<SchemaPath, Type> typedef : typedefs.entrySet()) {
166                 Type value = typedef.getValue();
167                 Type typeRef = new ReferencedTypeImpl(value.getPackageName(), value.getName());
168                 typeDefinitions.put(typeRef, value);
169                 TypeDefinition<?> schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.getKey());
170                 if (schemaNode != null) {
171
172                     typeToSchemaNode.put(typeRef, schemaNode);
173                 } else {
174                     LOG.error("Type definition for {} is not available", value);
175                 }
176             }
177             List<GeneratedTypeBuilder> augmentations = context.getAugmentations();
178             for (GeneratedTypeBuilder augmentation : augmentations) {
179                 typeToDefinition.put(augmentation, augmentation);
180             }
181             typeToAugmentation.putAll(context.getTypeToAugmentation());
182             for (GeneratedTypeBuilder augmentation : augmentations) {
183                 updatePromisedSchemas(augmentation);
184             }
185         }
186     }
187
188     @Override
189     public CompositeNode toDataDom(final DataObject data) {
190         return toCompositeNodeImpl(data);
191     }
192
193     @Override
194     public Entry<InstanceIdentifier, CompositeNode> toDataDom(
195             final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> entry) {
196         try {
197             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key = toDataDom(entry.getKey());
198             CompositeNode data;
199             if (Augmentation.class.isAssignableFrom(entry.getKey().getTargetType())) {
200                 data = toCompositeNodeImplAugument(key, entry.getValue());
201             } else {
202                 data = toCompositeNodeImpl(key, entry.getValue());
203             }
204             return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>(key,
205                     data);
206
207         } catch (Exception e) {
208             LOG.error("Error during serialization for {}.", entry.getKey(), e);
209             throw e;
210         }
211     }
212
213     private CompositeNode toCompositeNodeImpl(final DataObject object) {
214         Class<? extends DataContainer> cls = object.getImplementedInterface();
215         waitForSchema(cls);
216         DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
217         return codec.serialize(new ValueWithQName<DataObject>(null, object));
218     }
219
220     private CompositeNode toCompositeNodeImpl(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,
221             final DataObject object) {
222         PathArgument last = identifier.getPath().get(identifier.getPath().size() - 1);
223         Class<? extends DataContainer> cls = object.getImplementedInterface();
224         waitForSchema(cls);
225         DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
226         return codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
227     }
228
229     private CompositeNode toCompositeNodeImplAugument(
230             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, final DataObject object) {
231
232         // val cls = object.implementedInterface;
233         // waitForSchema(cls);
234         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument last = identifier.getPath().get(
235                 identifier.getPath().size() - 1);
236         AugmentationCodec codec = registry.getCodecForAugmentation((Class) object.getImplementedInterface());
237         CompositeNode ret = codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
238         if (last instanceof NodeIdentifierWithPredicates) {
239             NodeIdentifierWithPredicates predicates = (NodeIdentifierWithPredicates) last;
240             List<Node<?>> newNodes = new ArrayList<Node<?>>(predicates.getKeyValues().size());
241             for (Map.Entry<QName, Object> predicate : predicates.getKeyValues().entrySet()) {
242                 newNodes.add(new SimpleNodeTOImpl<Object>(predicate.getKey(), null, predicate.getValue()));
243             }
244             newNodes.addAll(ret.getValue());
245             return new CompositeNodeTOImpl(last.getNodeType(), null, newNodes);
246         }
247         return ret;
248     }
249
250     @Override
251     public void waitForSchema(final Class<?> cls) {
252         final ListenableFuture<Type> f = getSchemaDefinition(cls);
253         if (f != null) {
254             try {
255                 f.get();
256             } catch (InterruptedException | ExecutionException e) {
257                 LOG.warn("Waiting for schema for class {} failed", cls, e);
258                 throw new IllegalStateException(String.format("Failed to get schema for %s", cls), e);
259             }
260             LOG.info("Schema for {} became available, thread unblocked", cls);
261         }
262     }
263
264     @Override
265     public InstanceIdentifier toDataDom(
266             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path) {
267         for (final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
268             this.waitForSchema(arg.getType());
269         }
270
271         final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec();
272         Preconditions.checkState(c != null, "InstanceIdentifierCodec not present");
273         return c.serialize(path);
274     }
275
276     @Override
277     public DataObject dataObjectFromDataDom(
278             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path,
279             final CompositeNode domData) throws DeserializationException {
280         if (domData == null) {
281             return null;
282         }
283
284         try {
285             final Class<? extends DataContainer> container = path.getTargetType();
286             // FIXME: deprecate use without iid
287             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> wildcardedPath = createWildcarded(path);
288
289             final DataContainerCodec<? extends DataContainer> transformer = registry.getCodecForDataObject(container);
290             Preconditions.checkState(transformer != null, "Failed to find codec for type %s", container);
291
292             final ValueWithQName<? extends DataContainer> deserialize = transformer.deserialize(domData, wildcardedPath);
293             if (deserialize == null) {
294                 return null;
295             }
296
297             return (DataObject) deserialize.getValue();
298         } catch (Exception e) {
299             LOG.warn("Failed to deserialize path {} data {}", path, domData);
300             throw new DeserializationException("Data deserialization failed", e);
301         }
302     }
303
304     @Override
305     public org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends Object> fromDataDom(final InstanceIdentifier entry) throws DeserializationException {
306         try {
307             final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec();
308             Preconditions.checkState(c != null, "InstanceIdentifierCodec not present");
309             return c.deserialize(entry);
310         } catch (Exception e) {
311             LOG.warn("Failed to deserialize entry {}", entry);
312             throw new DeserializationException("Entry deserialization failed", e);
313         }
314     }
315
316     @Override
317     public CodecRegistry getCodecRegistry() {
318         return this.registry;
319     }
320
321     private void updateBindingFor(final Map<SchemaPath, GeneratedTypeBuilder> map, final SchemaContext module) {
322         for (Map.Entry<SchemaPath, GeneratedTypeBuilder> entry : map.entrySet()) {
323             SchemaNode schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.getKey());
324
325             // LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
326             Type typeRef = new ReferencedTypeImpl(entry.getValue().getPackageName(), entry.getValue().getName());
327             typeToDefinition.put(typeRef, entry.getValue());
328             if (schemaNode != null) {
329                 typeToSchemaNode.put(typeRef, schemaNode);
330                 updatePromisedSchemas(entry.getValue());
331             }
332         }
333     }
334
335
336     @Override
337     public Set<QName> getRpcQNamesFor(final Class<? extends RpcService> service) {
338         Set<QName> serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service
339                 .getSimpleName()));
340         if (serviceRef == null) {
341             serviceRef = Collections.emptySet();
342         }
343         return serviceRef;
344     }
345
346     private ListenableFuture<Type> getSchemaDefinition(final Class<?> cls) {
347         final Type type = Types.typeForClass(cls);
348         synchronized (this) {
349             if (typeToDefinition.containsKey(type)) {
350                 return null;
351             }
352
353             LOG.info("Thread is going to wait for schema for: {}", type.getFullyQualifiedName());
354             final SettableFuture<Type> f = SettableFuture.create();
355             promisedTypes.put(type, f);
356             return f;
357         }
358     }
359
360     @GuardedBy("this")
361     private void updatePromisedSchemas(final Type builder) {
362         final Type ref = new ReferencedTypeImpl(builder.getPackageName(), builder.getName());
363         final Collection<SettableFuture<Type>> futures = promisedTypes.get(ref);
364
365         if (futures != null) {
366             for (SettableFuture<Type> future : futures) {
367                 future.set(builder);
368             }
369             promisedTypes.removeAll(builder);
370         }
371     }
372
373     @Override
374     public void close() {
375         // Nothing to do
376     }
377
378     @Override
379     public DataContainer dataObjectFromDataDom(final Class<? extends DataContainer> container,
380             final CompositeNode domData) {
381         // FIXME: Add check for valids inputs
382         // which are Notification and Rpc Input / Rpc Output
383
384         org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataContainer> id = org.opendaylight.yangtools.yang.binding.InstanceIdentifier
385                 .create((Class) container);
386         Preconditions.checkNotNull(id, "Failed to create path for type %s", container);
387
388         try {
389             return dataObjectFromDataDom(id, domData);
390         } catch (DeserializationException e) {
391             LOG.warn("Conversion of class {} path {} data {} failed", container, id, domData, e);
392             throw new IllegalStateException("Failed to create data object", e);
393         }
394     }
395
396     @Override
397     public synchronized Optional<Class<? extends RpcService>> getRpcServiceClassFor(final String namespace, final String revision) {
398         Module module = null;
399         if (schemaContext != null) {
400             module = schemaContext.findModuleByNamespaceAndRevision(URI.create(namespace), QName.parseRevision(revision));
401         }
402         if (module == null) {
403             return Optional.absent();
404         }
405         try {
406             Optional<Type> rpcTypeName = getRpcServiceType(module);
407             if (rpcTypeName.isPresent()) {
408                 Class<?> rpcClass = classLoadingStrategy.loadClass(rpcTypeName.get().getFullyQualifiedName());
409                 return Optional.<Class<? extends RpcService>> of((Class<? extends RpcService>) rpcClass);
410             }
411         } catch (Exception e) {
412             LOG.debug("RPC class not present for {},{}", namespace, revision, e);
413         }
414         return Optional.absent();
415     }
416
417     public Optional<Type> getRpcServiceType(final Module module) {
418         String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
419         if (module.getRpcs().isEmpty()) {
420             return Optional.<Type> absent();
421         }
422         return Optional.<Type> of(new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
423                 + BindingMapping.RPC_SERVICE_SUFFIX));
424     }
425
426     @SuppressWarnings({ "rawtypes", "unchecked" })
427     private static final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> createWildcarded(
428             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path) {
429
430         LinkedList<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildcardedArgs = new LinkedList<>();
431         for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument pathArg : path.getPathArguments()) {
432             if(pathArg instanceof IdentifiableItem<?,?>) {
433                 pathArg = new Item(pathArg.getType());
434             }
435             wildcardedArgs.add(pathArg);
436         }
437         return org.opendaylight.yangtools.yang.binding.InstanceIdentifier.create(wildcardedArgs);
438     }
439
440     @Override
441     public final AugmentationSchema getAugmentation(final Type type) {
442         return typeToAugmentation.get(type);
443     }
444
445     @Override
446     public final GeneratedTypeBuilder getDefinition(final Type type) {
447         return typeToDefinition.get(type);
448     }
449
450     @Override
451     public final SchemaNode getSchemaNode(final Type type) {
452         return typeToSchemaNode.get(type);
453     }
454
455     @Override
456     public final GeneratedTypeBuilder getTypeBuilder(final SchemaPath path) {
457         return pathToType.get(path);
458     }
459 }