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.Collections;
13 import java.util.HashSet;
14 import java.util.LinkedList;
15 import java.util.List;
17 import java.util.Map.Entry;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Future;
24 import javassist.ClassPool;
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;
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;
73 public class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
74 SchemaLock, AutoCloseable, SchemaContextHolder {
76 private static final Logger LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl.class);
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;
85 // FIXME: how is this thread-safe?
86 private ClassPool pool;
88 // FIXME: how is this thread-safe?
90 private TransformerGenerator binding;
92 // FIXME: how is this thread-safe?
94 private LazyGeneratedCodecRegistry registry;
96 // FIXME: how is this thread-safe?
97 private SchemaContext schemaContext;
99 public RuntimeGeneratedMappingServiceImpl() {
100 this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
103 public RuntimeGeneratedMappingServiceImpl(final ClassLoadingStrategy strat) {
104 classLoadingStrategy = strat;
107 public ClassPool getPool() {
111 public void setPool(final ClassPool pool) {
116 public SchemaContext getSchemaContext() {
117 return schemaContext;
120 public void setSchemaContext(final SchemaContext schemaContext) {
121 this.schemaContext = schemaContext;
124 public TransformerGenerator getBinding() {
128 public void setBinding(final TransformerGenerator binding) {
129 this.binding = binding;
132 public LazyGeneratedCodecRegistry getRegistry() {
136 public void setRegistry(final LazyGeneratedCodecRegistry registry) {
137 this.registry = registry;
140 public ConcurrentMap<Type, GeneratedTypeBuilder> getTypeToDefinition() {
141 return typeToDefinition;
144 public ConcurrentMap<Type, Type> getTypeDefinitions() {
145 return typeDefinitions;
148 public ConcurrentMap<Type, SchemaNode> getTypeToSchemaNode() {
149 return typeToSchemaNode;
152 public ConcurrentMap<Type, Set<QName>> getServiceTypeToRpc() {
153 return serviceTypeToRpc;
157 public void onGlobalContextUpdated(final SchemaContext arg0) {
158 this.setSchemaContext(arg0);
159 this.recreateBindingContext(arg0);
160 LazyGeneratedCodecRegistry _registry = this.getRegistry();
161 _registry.onGlobalContextUpdated(arg0);
164 private void recreateBindingContext(final SchemaContext schemaContext) {
165 BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
166 newBinding.generateTypes(schemaContext);
168 for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
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);
178 if (!module.getRpcs().isEmpty()) {
179 Set<QName> rpcs = new HashSet<>();
180 for (RpcDefinition rpc : module.getRpcs()) {
181 rpcs.add(rpc.getQName());
183 Type serviceClass = new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
185 serviceTypeToRpc.put(serviceClass, rpcs);
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) {
196 binding.getTypeToSchemaNode().put(typeRef, schemaNode);
198 LOG.error("Type definition for {} is not available", value);
201 List<GeneratedTypeBuilder> augmentations = context.getAugmentations();
202 for (GeneratedTypeBuilder augmentation : augmentations) {
203 binding.getTypeToDefinition().put(augmentation, augmentation);
205 binding.getTypeToAugmentation().putAll(context.getTypeToAugmentation());
206 for (GeneratedTypeBuilder augmentation : augmentations) {
207 updatePromisedSchemas(augmentation);
213 public CompositeNode toDataDom(final DataObject data) {
214 return toCompositeNodeImpl(data);
218 public Entry<InstanceIdentifier, CompositeNode> toDataDom(
219 final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> entry) {
221 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key = toDataDom(entry.getKey());
223 if (Augmentation.class.isAssignableFrom(entry.getKey().getTargetType())) {
224 data = toCompositeNodeImplAugument(key, entry.getValue());
226 data = toCompositeNodeImpl(key, entry.getValue());
228 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>(key,
231 } catch (Exception e) {
232 LOG.error("Error during serialization for {}.", entry.getKey(), e);
237 private CompositeNode toCompositeNodeImpl(final DataObject object) {
238 Class<? extends DataContainer> cls = object.getImplementedInterface();
240 DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
241 return codec.serialize(new ValueWithQName<DataObject>(null, object));
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();
249 DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
250 return codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
253 private CompositeNode toCompositeNodeImplAugument(
254 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, final DataObject object) {
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()));
268 newNodes.addAll(ret.getChildren());
269 return new CompositeNodeTOImpl(last.getNodeType(), null, newNodes);
275 public void waitForSchema(final Class class1) {
276 if (registry.isCodecAvailable(class1)) {
279 Type ref = Types.typeForClass(class1);
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);
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());
295 final InstanceIdentifierCodec c = getRegistry().getInstanceIdentifierCodec();
296 Preconditions.checkState(c != null, "InstanceIdentifierCodec not present");
297 return c.serialize(path);
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) {
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);
313 final DataContainerCodec<? extends DataContainer> transformer = getRegistry().getCodecForDataObject(container);
314 Preconditions.checkState(transformer != null, "Failed to find codec for type %s", container);
316 final ValueWithQName<? extends DataContainer> deserialize = transformer.deserialize(domData, wildcardedPath);
317 if (deserialize == null) {
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);
329 public org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends Object> fromDataDom(final InstanceIdentifier entry) throws DeserializationException {
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);
341 public CodecRegistry getCodecRegistry() {
342 return this.getRegistry();
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());
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());
360 binding = new TransformerGenerator(pool);
361 registry = new LazyGeneratedCodecRegistry(this, classLoadingStrategy);
363 registry.setGenerator(binding);
364 // binding.staticFieldsInitializer = registry
365 binding.setListener(registry);
366 binding.setTypeToDefinition(typeToDefinition);
367 binding.setTypeToSchemaNode(typeToSchemaNode);
368 binding.setTypeDefinitions(typeDefinitions);
370 // if (ctx !== null) {
371 // listenerRegistration = ctx.registerService(SchemaServiceListener,
372 // this, new Hashtable<String, String>());
377 public Set<QName> getRpcQNamesFor(final Class<? extends RpcService> service) {
378 Set<QName> serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service
380 if (serviceRef == null) {
381 serviceRef = Collections.emptySet();
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());
394 private Future<Type> waitForTypeDefinition(final Type type) {
395 final SettableFuture<Type> future = SettableFuture.<Type> create();
396 promisedTypes.put(type, future);
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()) {
406 for (SettableFuture<Type> future : futures) {
409 promisedTypes.removeAll(builder);
413 public void close() {
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
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);
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);
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));
441 if (module == null) {
442 return Optional.absent();
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);
451 } catch (Exception e) {
452 LOG.debug("RPC class not present for {},{}", namespace, revision, e);
454 return Optional.absent();
457 public Optional<Type> getRpcServiceType(final Module module) {
458 String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
459 if (module.getRpcs().isEmpty()) {
460 return Optional.<Type> absent();
462 return Optional.<Type> of(new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
463 + BindingMapping.RPC_SERVICE_SUFFIX));
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) {
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());
475 wildcardedArgs.add(pathArg);
477 return org.opendaylight.yangtools.yang.binding.InstanceIdentifier.create(wildcardedArgs);