/* * 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 javassist.ClassPool import org.opendaylight.yangtools.yang.model.api.SchemaContext import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl import java.util.Map 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.model.api.SchemaNode import java.util.concurrent.ConcurrentHashMap import org.opendaylight.yangtools.yang.data.api.CompositeNode import org.opendaylight.yangtools.yang.binding.DataObject import org.opendaylight.yangtools.yang.binding.InstanceIdentifier import java.util.Map.Entry import java.util.AbstractMap.SimpleEntry import org.opendaylight.yangtools.yang.model.api.SchemaPath import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil import org.opendaylight.yangtools.yang.binding.DataContainer import java.util.concurrent.ConcurrentMap import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType import com.google.common.collect.HashMultimap import com.google.common.util.concurrent.SettableFuture import java.util.concurrent.Future import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService import org.slf4j.LoggerFactory import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec import org.opendaylight.yangtools.binding.generator.util.Types //import org.osgi.framework.BundleContext import java.util.Hashtable //import org.osgi.framework.ServiceRegistration import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException import java.util.concurrent.Callable import org.opendaylight.yangtools.yang.binding.Augmentation import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates import java.util.ArrayList import org.opendaylight.yangtools.yang.data.api.Node import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl import org.opendaylight.yangtools.yang.binding.RpcService import java.util.Set import org.opendaylight.yangtools.yang.common.QName import com.google.common.collect.FluentIterable import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil import java.util.HashMap class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, SchemaLock, AutoCloseable { @Property ClassPool pool; private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl); @Property extension TransformerGenerator binding; @Property extension LazyGeneratedCodecRegistry registry; @Property val ConcurrentMap typeDefinitions = new ConcurrentHashMap(); @Property val ConcurrentMap typeToDefinition = new ConcurrentHashMap(); @Property val ConcurrentMap typeToSchemaNode = new ConcurrentHashMap(); @Property val ConcurrentMap> serviceTypeToRpc = new ConcurrentHashMap(); val promisedTypeDefinitions = HashMultimap.>create; val promisedTypes = HashMultimap.>create; //ServiceRegistration listenerRegistration override onGlobalContextUpdated(SchemaContext arg0) { recreateBindingContext(arg0); registry.onGlobalContextUpdated(arg0); } def recreateBindingContext(SchemaContext schemaContext) { val newBinding = new BindingGeneratorImpl(); newBinding.generateTypes(schemaContext); for (entry : newBinding.moduleContexts.entrySet) { registry.onModuleContextAdded(schemaContext, entry.key, entry.value); binding.pathToType.putAll(entry.value.childNodes) val module = entry.key; val context = entry.value; updateBindingFor(context.childNodes, schemaContext); updateBindingFor(context.cases, schemaContext); val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module); if(!module.rpcs.empty) { val rpcs = FluentIterable.from(module.rpcs).transform[QName].toSet val serviceClass = new ReferencedTypeImpl(namespace,BindingGeneratorUtil.parseToClassName(module.name)+"Service"); serviceTypeToRpc.put(serviceClass,rpcs); } val typedefs = context.typedefs; for (typedef : typedefs.entrySet) { val typeRef = new ReferencedTypeImpl(typedef.value.packageName,typedef.value.name) binding.typeDefinitions.put(typeRef, typedef.value as GeneratedType); val schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext,typedef.key); if(schemaNode != null) { binding.typeToSchemaNode.put(typeRef,schemaNode); } else { LOG.error("Type definition for {} is not available",typedef.value); } } val augmentations = context.augmentations; for (augmentation : augmentations) { binding.typeToDefinition.put(augmentation, augmentation); } binding.typeToAugmentation.putAll(context.typeToAugmentation); for(augmentation : augmentations) { updatePromisedSchemas(augmentation); } } } override CompositeNode toDataDom(DataObject data) { toCompositeNodeImpl(data); } override Entry toDataDom( Entry, DataObject> entry) { try { val key = toDataDom(entry.key) var CompositeNode data; if(Augmentation.isAssignableFrom(entry.key.targetType)) { data = toCompositeNodeImplAugument(key,entry.value); } else { data = toCompositeNodeImpl(key,entry.value); } return new SimpleEntry(key, data); } catch (Exception e) { LOG.error("Error during serialization for {}.", entry.key,e); throw e; } } private def CompositeNode toCompositeNodeImpl(DataObject object) { val cls = object.implementedInterface; waitForSchema(cls); val codec = registry.getCodecForDataObject(cls) as DataContainerCodec; val ret = codec.serialize(new ValueWithQName(null, object)); return ret as CompositeNode; } private def CompositeNode toCompositeNodeImpl(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,DataObject object) { val last = identifier.path.last; val cls = object.implementedInterface; waitForSchema(cls); val codec = registry.getCodecForDataObject(cls) as DataContainerCodec; val ret = codec.serialize(new ValueWithQName(last.nodeType, object)); return ret as CompositeNode; } private def CompositeNode toCompositeNodeImplAugument(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,DataObject object) { //val cls = object.implementedInterface; //waitForSchema(cls); val last = identifier.path.last; val codec = registry.getCodecForAugmentation(object.implementedInterface as Class) as AugmentationCodec; val ret = codec.serialize(new ValueWithQName(last.nodeType, object)); if(last instanceof NodeIdentifierWithPredicates) { val predicates = last as NodeIdentifierWithPredicates; val newNodes = new ArrayList>(predicates.keyValues.size); for(predicate : predicates.keyValues.entrySet) { newNodes.add(new SimpleNodeTOImpl(predicate.key,null,predicate.value)); } newNodes.addAll(ret.children); return new CompositeNodeTOImpl(last.nodeType,null,newNodes); } return ret as CompositeNode; } override waitForSchema(Class class1) { if(registry.isCodecAvailable(class1)) { return; } val ref = Types.typeForClass(class1); getSchemaWithRetry(ref); } override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom( InstanceIdentifier path) { for (arg : path.path) { waitForSchema(arg.type); } return registry.instanceIdentifierCodec.serialize(path); } override dataObjectFromDataDom(InstanceIdentifier path, CompositeNode node) { dataObjectFromDataDom(path.targetType,node) as DataObject; } override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) { return tryDeserialization[ | registry.instanceIdentifierCodec.deserialize(entry); ] } override getCodecRegistry() { return getRegistry(); } private static def T tryDeserialization(Callable deserializationBlock) throws DeserializationException { try { deserializationBlock.call() } catch (Exception e) { // FIXME: Make this block providing more information. throw new DeserializationException(e); } } private def void updateBindingFor(Map map, SchemaContext module) { for (entry : map.entrySet) { val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key); //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName) val typeRef = new ReferencedTypeImpl(entry.value.packageName,entry.value.name) typeToDefinition.put(typeRef, entry.value); if (schemaNode != null) { typeToSchemaNode.put(typeRef, schemaNode); updatePromisedSchemas(entry.value); } } } public def void init() { binding = new TransformerGenerator(pool); registry = new LazyGeneratedCodecRegistry(this) registry.generator = binding //binding.staticFieldsInitializer = registry binding.listener = registry binding.typeToDefinition = typeToDefinition binding.typeToSchemaNode = typeToSchemaNode binding.typeDefinitions = typeDefinitions // if (ctx !== null) { // listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable()); // } } override getRpcQNamesFor(Class service) { return serviceTypeToRpc.get(new ReferencedTypeImpl(service.package.name,service.simpleName)); } private def void getSchemaWithRetry(Type type) { if (typeToDefinition.containsKey(type)) { return; } LOG.info("Thread blocked waiting for schema for: {}",type.fullyQualifiedName) type.waitForTypeDefinition.get(); LOG.info("Schema for {} became available, thread unblocked",type.fullyQualifiedName) } private def Future waitForTypeDefinition(Type type) { val future = SettableFuture.create() promisedTypes.put(type, future); return future; } private def void updatePromisedSchemas(Type builder) { val ref = new ReferencedTypeImpl(builder.packageName, builder.name); val futures = promisedTypes.get(ref); if (futures === null || futures.empty) { return; } for (future : futures) { future.set(builder); } promisedTypes.removeAll(builder); } override close() throws Exception { //listenerRegistration?.unregister(); } override dataObjectFromDataDom(Class container, CompositeNode domData) { return tryDeserialization[ | if (domData == null) { return null; } val transformer = registry.getCodecForDataObject(container); val ret = transformer.deserialize(domData)?.value as DataObject; return ret; ] } }