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