/** * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.sal.binding.generator.impl; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javassist.ClassPool; import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; import org.opendaylight.yangtools.binding.generator.util.Types; import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; import org.opendaylight.yangtools.yang.binding.RpcService; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry; import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec; import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec; import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.util.concurrent.SettableFuture; public class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener, SchemaLock, AutoCloseable, SchemaContextHolder { private static final Logger LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl.class); private final ConcurrentMap typeDefinitions = new ConcurrentHashMap<>(); private final ConcurrentMap typeToDefinition = new ConcurrentHashMap<>(); private final ConcurrentMap typeToSchemaNode = new ConcurrentHashMap<>(); private final ConcurrentMap> serviceTypeToRpc = new ConcurrentHashMap<>(); private final HashMultimap> promisedTypes = HashMultimap.create(); private final ClassLoadingStrategy classLoadingStrategy; // FIXME: will become final private ClassPool pool; private TransformerGenerator binding; private LazyGeneratedCodecRegistry registry; private SchemaContext schemaContext; @Deprecated public RuntimeGeneratedMappingServiceImpl() { this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy()); } @Deprecated public RuntimeGeneratedMappingServiceImpl(final ClassLoadingStrategy strat) { classLoadingStrategy = strat; } public RuntimeGeneratedMappingServiceImpl(final ClassPool pool) { this(pool, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy()); } public RuntimeGeneratedMappingServiceImpl(final ClassPool pool, final ClassLoadingStrategy strat) { this.pool = Preconditions.checkNotNull(pool); this.classLoadingStrategy = Preconditions.checkNotNull(strat); // FIXME: merge into constructor once legacy init() is removed doInit(); } private void doInit() { binding = new TransformerGenerator(pool); registry = new LazyGeneratedCodecRegistry(this, classLoadingStrategy); registry.setGenerator(binding); // binding.staticFieldsInitializer = registry binding.setListener(registry); binding.setTypeToDefinition(typeToDefinition); binding.setTypeToSchemaNode(typeToSchemaNode); binding.setTypeDefinitions(typeDefinitions); // if (ctx !== null) { // listenerRegistration = ctx.registerService(SchemaServiceListener, // this, new Hashtable()); // } } @Deprecated public void setPool(final ClassPool pool) { this.pool = pool; } @Override public synchronized SchemaContext getSchemaContext() { return schemaContext; } @Override public synchronized void onGlobalContextUpdated(final SchemaContext context) { this.schemaContext = context; this.recreateBindingContext(context); this.registry.onGlobalContextUpdated(context); } private void recreateBindingContext(final SchemaContext schemaContext) { BindingGeneratorImpl newBinding = new BindingGeneratorImpl(); newBinding.generateTypes(schemaContext); for (Map.Entry entry : newBinding.getModuleContexts().entrySet()) { registry.onModuleContextAdded(schemaContext, entry.getKey(), entry.getValue()); binding.getPathToType().putAll(entry.getValue().getChildNodes()); Module module = entry.getKey(); ModuleContext context = entry.getValue(); updateBindingFor(context.getChildNodes(), schemaContext); updateBindingFor(context.getCases(), schemaContext); String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module); if (!module.getRpcs().isEmpty()) { Set rpcs = new HashSet<>(); for (RpcDefinition rpc : module.getRpcs()) { rpcs.add(rpc.getQName()); } Type serviceClass = new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName()) + "Service"); serviceTypeToRpc.put(serviceClass, rpcs); } Map typedefs = context.getTypedefs(); for (Map.Entry typedef : typedefs.entrySet()) { Type value = typedef.getValue(); Type typeRef = new ReferencedTypeImpl(value.getPackageName(), value.getName()); binding.getTypeDefinitions().put(typeRef, value); TypeDefinition schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.getKey()); if (schemaNode != null) { binding.getTypeToSchemaNode().put(typeRef, schemaNode); } else { LOG.error("Type definition for {} is not available", value); } } List augmentations = context.getAugmentations(); for (GeneratedTypeBuilder augmentation : augmentations) { binding.getTypeToDefinition().put(augmentation, augmentation); } binding.getTypeToAugmentation().putAll(context.getTypeToAugmentation()); for (GeneratedTypeBuilder augmentation : augmentations) { updatePromisedSchemas(augmentation); } } } @Override public CompositeNode toDataDom(final DataObject data) { return toCompositeNodeImpl(data); } @Override public Entry toDataDom( final Entry, DataObject> entry) { try { org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key = toDataDom(entry.getKey()); CompositeNode data; if (Augmentation.class.isAssignableFrom(entry.getKey().getTargetType())) { data = toCompositeNodeImplAugument(key, entry.getValue()); } else { data = toCompositeNodeImpl(key, entry.getValue()); } return new SimpleEntry(key, data); } catch (Exception e) { LOG.error("Error during serialization for {}.", entry.getKey(), e); throw e; } } private CompositeNode toCompositeNodeImpl(final DataObject object) { Class cls = object.getImplementedInterface(); waitForSchema(cls); DataContainerCodec codec = (DataContainerCodec) registry.getCodecForDataObject(cls); return codec.serialize(new ValueWithQName(null, object)); } private CompositeNode toCompositeNodeImpl(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, final DataObject object) { PathArgument last = identifier.getPath().get(identifier.getPath().size() - 1); Class cls = object.getImplementedInterface(); waitForSchema(cls); DataContainerCodec codec = (DataContainerCodec) registry.getCodecForDataObject(cls); return codec.serialize(new ValueWithQName(last.getNodeType(), object)); } private CompositeNode toCompositeNodeImplAugument( final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, final DataObject object) { // val cls = object.implementedInterface; // waitForSchema(cls); org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument last = identifier.getPath().get( identifier.getPath().size() - 1); AugmentationCodec codec = registry.getCodecForAugmentation((Class) object.getImplementedInterface()); CompositeNode ret = codec.serialize(new ValueWithQName(last.getNodeType(), object)); if (last instanceof NodeIdentifierWithPredicates) { NodeIdentifierWithPredicates predicates = (NodeIdentifierWithPredicates) last; List> newNodes = new ArrayList>(predicates.getKeyValues().size()); for (Map.Entry predicate : predicates.getKeyValues().entrySet()) { newNodes.add(new SimpleNodeTOImpl(predicate.getKey(), null, predicate.getValue())); } newNodes.addAll(ret.getChildren()); return new CompositeNodeTOImpl(last.getNodeType(), null, newNodes); } return ret; } @Override public void waitForSchema(final Class class1) { if (registry.isCodecAvailable(class1)) { return; } Type ref = Types.typeForClass(class1); try { getSchemaWithRetry(ref); } catch (InterruptedException | ExecutionException e) { LOG.warn("Waiting for schema for class {} failed", class1, e); throw new IllegalStateException(String.format("Failed to get schema for {}", class1), e); } } @Override public InstanceIdentifier toDataDom( final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { for (final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : path.getPathArguments()) { this.waitForSchema(arg.getType()); } final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec(); Preconditions.checkState(c != null, "InstanceIdentifierCodec not present"); return c.serialize(path); } @Override public DataObject dataObjectFromDataDom( final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path, final CompositeNode domData) throws DeserializationException { if (domData == null) { return null; } try { final Class container = path.getTargetType(); // FIXME: deprecate use without iid final org.opendaylight.yangtools.yang.binding.InstanceIdentifier wildcardedPath = createWildcarded(path); final DataContainerCodec transformer = registry.getCodecForDataObject(container); Preconditions.checkState(transformer != null, "Failed to find codec for type %s", container); final ValueWithQName deserialize = transformer.deserialize(domData, wildcardedPath); if (deserialize == null) { return null; } return (DataObject) deserialize.getValue(); } catch (Exception e) { LOG.warn("Failed to deserialize path {} data {}", path, domData); throw new DeserializationException("Data deserialization failed", e); } } @Override public org.opendaylight.yangtools.yang.binding.InstanceIdentifier fromDataDom(final InstanceIdentifier entry) throws DeserializationException { try { final InstanceIdentifierCodec c = registry.getInstanceIdentifierCodec(); Preconditions.checkState(c != null, "InstanceIdentifierCodec not present"); return c.deserialize(entry); } catch (Exception e) { LOG.warn("Failed to deserialize entry {}", entry); throw new DeserializationException("Entry deserialization failed", e); } } @Override public CodecRegistry getCodecRegistry() { return this.registry; } private void updateBindingFor(final Map map, final SchemaContext module) { for (Map.Entry entry : map.entrySet()) { SchemaNode schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.getKey()); // LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName) Type typeRef = new ReferencedTypeImpl(entry.getValue().getPackageName(), entry.getValue().getName()); typeToDefinition.put(typeRef, entry.getValue()); if (schemaNode != null) { typeToSchemaNode.put(typeRef, schemaNode); updatePromisedSchemas(entry.getValue()); } } } @Deprecated public void init() { doInit(); } @Override public Set getRpcQNamesFor(final Class service) { Set serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service .getSimpleName())); if (serviceRef == null) { serviceRef = Collections.emptySet(); } return serviceRef; } private void getSchemaWithRetry(final Type type) throws InterruptedException, ExecutionException { if (!typeToDefinition.containsKey(type)) { LOG.info("Thread blocked waiting for schema for: {}", type.getFullyQualifiedName()); waitForTypeDefinition(type).get(); LOG.info("Schema for {} became available, thread unblocked", type.getFullyQualifiedName()); } } private Future waitForTypeDefinition(final Type type) { final SettableFuture future = SettableFuture. create(); promisedTypes.put(type, future); return future; } private void updatePromisedSchemas(final Type builder) { Type ref = new ReferencedTypeImpl(builder.getPackageName(), builder.getName()); Set> futures = promisedTypes.get(ref); if (futures == null || futures.isEmpty()) { return; } for (SettableFuture future : futures) { future.set(builder); } promisedTypes.removeAll(builder); } @Override public void close() { // Nothing to do } @Override public DataContainer dataObjectFromDataDom(final Class container, final CompositeNode domData) { // FIXME: Add check for valids inputs // which are Notification and Rpc Input / Rpc Output org.opendaylight.yangtools.yang.binding.InstanceIdentifier id = org.opendaylight.yangtools.yang.binding.InstanceIdentifier .create((Class) container); Preconditions.checkNotNull(id, "Failed to create path for type %s", container); try { return dataObjectFromDataDom(id, domData); } catch (DeserializationException e) { LOG.warn("Conversion of class {} path {} data {} failed", container, id, domData, e); throw new IllegalStateException("Failed to create data object", e); } } @Override public synchronized Optional> getRpcServiceClassFor(final String namespace, final String revision) { Module module = null; if (schemaContext != null) { module = schemaContext.findModuleByName(namespace, QName.parseRevision(revision)); } if (module == null) { return Optional.absent(); } try { Optional rpcTypeName = getRpcServiceType(module); if (rpcTypeName.isPresent()) { Class rpcClass = binding.getClassLoadingStrategy().loadClass( rpcTypeName.get().getFullyQualifiedName()); return Optional.> of((Class) rpcClass); } } catch (Exception e) { LOG.debug("RPC class not present for {},{}", namespace, revision, e); } return Optional.absent(); } public Optional getRpcServiceType(final Module module) { String namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module); if (module.getRpcs().isEmpty()) { return Optional. absent(); } return Optional. of(new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName()) + BindingMapping.RPC_SERVICE_SUFFIX)); } @SuppressWarnings({ "rawtypes", "unchecked" }) private static final org.opendaylight.yangtools.yang.binding.InstanceIdentifier createWildcarded( final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { LinkedList wildcardedArgs = new LinkedList<>(); for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument pathArg : path.getPathArguments()) { if(pathArg instanceof IdentifiableItem) { pathArg = new Item(pathArg.getType()); } wildcardedArgs.add(pathArg); } return org.opendaylight.yangtools.yang.binding.InstanceIdentifier.create(wildcardedArgs); } }