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