2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.sal.binding.generator.impl;
10 import java.util.AbstractMap.SimpleEntry;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.LinkedList;
16 import java.util.List;
18 import java.util.Map.Entry;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.ExecutionException;
24 import javassist.ClassPool;
26 import javax.annotation.concurrent.GuardedBy;
28 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
29 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
30 import org.opendaylight.yangtools.binding.generator.util.Types;
31 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
32 import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils;
33 import org.opendaylight.yangtools.sal.binding.model.api.Type;
34 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
35 import org.opendaylight.yangtools.yang.binding.Augmentation;
36 import org.opendaylight.yangtools.yang.binding.BindingMapping;
37 import org.opendaylight.yangtools.yang.binding.DataContainer;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
41 import org.opendaylight.yangtools.yang.binding.RpcService;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
44 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
46 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
47 import org.opendaylight.yangtools.yang.data.api.Node;
48 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
49 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
50 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
51 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
52 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
53 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
54 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
55 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
56 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
57 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
58 import org.opendaylight.yangtools.yang.model.api.Module;
59 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
60 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
61 import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder;
62 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
63 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
65 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
66 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
70 import com.google.common.base.Optional;
71 import com.google.common.base.Preconditions;
72 import com.google.common.collect.HashMultimap;
73 import com.google.common.collect.Multimap;
74 import com.google.common.util.concurrent.ListenableFuture;
75 import com.google.common.util.concurrent.SettableFuture;
77 public class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
78 SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
80 private static final Logger LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl.class);
82 private final ConcurrentMap<Type, Set<QName>> serviceTypeToRpc = new ConcurrentHashMap<>();
85 * This is map of types which users are waiting for.
88 private final Multimap<Type, SettableFuture<Type>> promisedTypes = HashMultimap.create();
90 private final ClassLoadingStrategy classLoadingStrategy;
92 // FIXME: will become final
93 private ClassPool pool;
94 private AbstractTransformerGenerator binding;
95 private LazyGeneratedCodecRegistry registry;
98 * FIXME: updated here, access from AbstractTransformer
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<>();
105 // FIXME: need to figure these out
106 private final ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap<>();
107 private SchemaContext schemaContext;
110 public RuntimeGeneratedMappingServiceImpl() {
111 this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
115 public RuntimeGeneratedMappingServiceImpl(final ClassLoadingStrategy strat) {
116 classLoadingStrategy = strat;
119 public RuntimeGeneratedMappingServiceImpl(final ClassPool pool) {
120 this(pool, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
123 public RuntimeGeneratedMappingServiceImpl(final ClassPool pool, final ClassLoadingStrategy strat) {
124 this.pool = Preconditions.checkNotNull(pool);
125 this.classLoadingStrategy = Preconditions.checkNotNull(strat);
127 // FIXME: merge into constructor once legacy init() is removed
131 private void doInit() {
132 binding = new TransformerGenerator(this, pool);
133 registry = new LazyGeneratedCodecRegistry(this, binding, classLoadingStrategy);
134 binding.setListener(registry);
136 // if (ctx !== null) {
137 // listenerRegistration = ctx.registerService(SchemaServiceListener,
138 // this, new Hashtable<String, String>());
143 public void setPool(final ClassPool pool) {
148 public synchronized SchemaContext getSchemaContext() {
149 return schemaContext;
153 public synchronized void onGlobalContextUpdated(final SchemaContext context) {
154 this.schemaContext = Preconditions.checkNotNull(context);
155 this.recreateBindingContext(context);
156 this.registry.onGlobalContextUpdated(context);
160 private void recreateBindingContext(final SchemaContext schemaContext) {
161 BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
162 newBinding.generateTypes(schemaContext);
164 for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
166 registry.onModuleContextAdded(schemaContext, entry.getKey(), entry.getValue());
167 pathToType.putAll(entry.getValue().getChildNodes());
168 Module module = entry.getKey();
169 ModuleContext context = entry.getValue();
170 updateBindingFor(context.getChildNodes(), schemaContext);
171 updateBindingFor(context.getCases(), schemaContext);
172 String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
174 if (!module.getRpcs().isEmpty()) {
175 Set<QName> rpcs = new HashSet<>();
176 for (RpcDefinition rpc : module.getRpcs()) {
177 rpcs.add(rpc.getQName());
179 Type serviceClass = new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
181 serviceTypeToRpc.put(serviceClass, rpcs);
184 Map<SchemaPath, Type> typedefs = context.getTypedefs();
185 for (Map.Entry<SchemaPath, Type> typedef : typedefs.entrySet()) {
186 Type value = typedef.getValue();
187 Type typeRef = new ReferencedTypeImpl(value.getPackageName(), value.getName());
188 typeDefinitions.put(typeRef, value);
189 TypeDefinition<?> schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.getKey());
190 if (schemaNode != null) {
192 typeToSchemaNode.put(typeRef, schemaNode);
194 LOG.error("Type definition for {} is not available", value);
197 List<GeneratedTypeBuilder> augmentations = context.getAugmentations();
198 for (GeneratedTypeBuilder augmentation : augmentations) {
199 typeToDefinition.put(augmentation, augmentation);
201 typeToAugmentation.putAll(context.getTypeToAugmentation());
202 for (GeneratedTypeBuilder augmentation : augmentations) {
203 updatePromisedSchemas(augmentation);
209 public CompositeNode toDataDom(final DataObject data) {
210 return toCompositeNodeImpl(data);
214 public Entry<InstanceIdentifier, CompositeNode> toDataDom(
215 final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> entry) {
217 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key = toDataDom(entry.getKey());
219 if (Augmentation.class.isAssignableFrom(entry.getKey().getTargetType())) {
220 data = toCompositeNodeImplAugument(key, entry.getValue());
222 data = toCompositeNodeImpl(key, entry.getValue());
224 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>(key,
227 } catch (Exception e) {
228 LOG.error("Error during serialization for {}.", entry.getKey(), e);
233 private CompositeNode toCompositeNodeImpl(final DataObject object) {
234 Class<? extends DataContainer> cls = object.getImplementedInterface();
236 DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
237 return codec.serialize(new ValueWithQName<DataObject>(null, object));
240 private CompositeNode toCompositeNodeImpl(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,
241 final DataObject object) {
242 PathArgument last = identifier.getPath().get(identifier.getPath().size() - 1);
243 Class<? extends DataContainer> cls = object.getImplementedInterface();
245 DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
246 return codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
249 private CompositeNode toCompositeNodeImplAugument(
250 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, final DataObject object) {
252 // val cls = object.implementedInterface;
253 // waitForSchema(cls);
254 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument last = identifier.getPath().get(
255 identifier.getPath().size() - 1);
256 AugmentationCodec codec = registry.getCodecForAugmentation((Class) object.getImplementedInterface());
257 CompositeNode ret = codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
258 if (last instanceof NodeIdentifierWithPredicates) {
259 NodeIdentifierWithPredicates predicates = (NodeIdentifierWithPredicates) last;
260 List<Node<?>> newNodes = new ArrayList<Node<?>>(predicates.getKeyValues().size());
261 for (Map.Entry<QName, Object> predicate : predicates.getKeyValues().entrySet()) {
262 newNodes.add(new SimpleNodeTOImpl<Object>(predicate.getKey(), null, predicate.getValue()));
264 newNodes.addAll(ret.getChildren());
265 return new CompositeNodeTOImpl(last.getNodeType(), null, newNodes);
271 public void waitForSchema(final Class<?> cls) {
272 final ListenableFuture<Type> f = getSchemaDefinition(cls);
276 } catch (InterruptedException | ExecutionException e) {
277 LOG.warn("Waiting for schema for class {} failed", cls, e);
278 throw new IllegalStateException(String.format("Failed to get schema for %s", cls), e);
280 LOG.info("Schema for {} became available, thread unblocked", cls);
285 public InstanceIdentifier toDataDom(
286 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path) {
287 for (final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
288 this.waitForSchema(arg.getType());
291 final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec();
292 Preconditions.checkState(c != null, "InstanceIdentifierCodec not present");
293 return c.serialize(path);
297 public DataObject dataObjectFromDataDom(
298 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path,
299 final CompositeNode domData) throws DeserializationException {
300 if (domData == null) {
305 final Class<? extends DataContainer> container = path.getTargetType();
306 // FIXME: deprecate use without iid
307 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> wildcardedPath = createWildcarded(path);
309 final DataContainerCodec<? extends DataContainer> transformer = registry.getCodecForDataObject(container);
310 Preconditions.checkState(transformer != null, "Failed to find codec for type %s", container);
312 final ValueWithQName<? extends DataContainer> deserialize = transformer.deserialize(domData, wildcardedPath);
313 if (deserialize == null) {
317 return (DataObject) deserialize.getValue();
318 } catch (Exception e) {
319 LOG.warn("Failed to deserialize path {} data {}", path, domData);
320 throw new DeserializationException("Data deserialization failed", e);
325 public org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends Object> fromDataDom(final InstanceIdentifier entry) throws DeserializationException {
327 final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec();
328 Preconditions.checkState(c != null, "InstanceIdentifierCodec not present");
329 return c.deserialize(entry);
330 } catch (Exception e) {
331 LOG.warn("Failed to deserialize entry {}", entry);
332 throw new DeserializationException("Entry deserialization failed", e);
337 public CodecRegistry getCodecRegistry() {
338 return this.registry;
341 private void updateBindingFor(final Map<SchemaPath, GeneratedTypeBuilder> map, final SchemaContext module) {
342 for (Map.Entry<SchemaPath, GeneratedTypeBuilder> entry : map.entrySet()) {
343 SchemaNode schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.getKey());
345 // LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
346 Type typeRef = new ReferencedTypeImpl(entry.getValue().getPackageName(), entry.getValue().getName());
347 typeToDefinition.put(typeRef, entry.getValue());
348 if (schemaNode != null) {
349 typeToSchemaNode.put(typeRef, schemaNode);
350 updatePromisedSchemas(entry.getValue());
361 public Set<QName> getRpcQNamesFor(final Class<? extends RpcService> service) {
362 Set<QName> serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service
364 if (serviceRef == null) {
365 serviceRef = Collections.emptySet();
370 private ListenableFuture<Type> getSchemaDefinition(final Class<?> cls) {
371 final Type type = Types.typeForClass(cls);
372 synchronized (this) {
373 if (typeToDefinition.containsKey(type)) {
377 LOG.info("Thread is going to wait for schema for: {}", type.getFullyQualifiedName());
378 final SettableFuture<Type> f = SettableFuture.create();
379 promisedTypes.put(type, f);
385 private void updatePromisedSchemas(final Type builder) {
386 final Type ref = new ReferencedTypeImpl(builder.getPackageName(), builder.getName());
387 final Collection<SettableFuture<Type>> futures = promisedTypes.get(ref);
389 if (futures != null) {
390 for (SettableFuture<Type> future : futures) {
393 promisedTypes.removeAll(builder);
398 public void close() {
403 public DataContainer dataObjectFromDataDom(final Class<? extends DataContainer> container,
404 final CompositeNode domData) {
405 // FIXME: Add check for valids inputs
406 // which are Notification and Rpc Input / Rpc Output
408 org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataContainer> id = org.opendaylight.yangtools.yang.binding.InstanceIdentifier
409 .create((Class) container);
410 Preconditions.checkNotNull(id, "Failed to create path for type %s", container);
413 return dataObjectFromDataDom(id, domData);
414 } catch (DeserializationException e) {
415 LOG.warn("Conversion of class {} path {} data {} failed", container, id, domData, e);
416 throw new IllegalStateException("Failed to create data object", e);
421 public synchronized Optional<Class<? extends RpcService>> getRpcServiceClassFor(final String namespace, final String revision) {
422 Module module = null;
423 if (schemaContext != null) {
424 module = schemaContext.findModuleByName(namespace, QName.parseRevision(revision));
426 if (module == null) {
427 return Optional.absent();
430 Optional<Type> rpcTypeName = getRpcServiceType(module);
431 if (rpcTypeName.isPresent()) {
432 Class<?> rpcClass = classLoadingStrategy.loadClass(rpcTypeName.get().getFullyQualifiedName());
433 return Optional.<Class<? extends RpcService>> of((Class<? extends RpcService>) rpcClass);
435 } catch (Exception e) {
436 LOG.debug("RPC class not present for {},{}", namespace, revision, e);
438 return Optional.absent();
441 public Optional<Type> getRpcServiceType(final Module module) {
442 String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
443 if (module.getRpcs().isEmpty()) {
444 return Optional.<Type> absent();
446 return Optional.<Type> of(new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
447 + BindingMapping.RPC_SERVICE_SUFFIX));
450 @SuppressWarnings({ "rawtypes", "unchecked" })
451 private static final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> createWildcarded(
452 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> path) {
454 LinkedList<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildcardedArgs = new LinkedList<>();
455 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument pathArg : path.getPathArguments()) {
456 if(pathArg instanceof IdentifiableItem<?,?>) {
457 pathArg = new Item(pathArg.getType());
459 wildcardedArgs.add(pathArg);
461 return org.opendaylight.yangtools.yang.binding.InstanceIdentifier.create(wildcardedArgs);
465 public final AugmentationSchema getAugmentation(final Type type) {
466 return typeToAugmentation.get(type);
470 public final GeneratedTypeBuilder getDefinition(final Type type) {
471 return typeToDefinition.get(type);
475 public final SchemaNode getSchemaNode(final Type type) {
476 return typeToSchemaNode.get(type);
480 public final GeneratedTypeBuilder getTypeBuilder(final SchemaPath path) {
481 return pathToType.get(path);