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