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 com.google.common.base.Optional
11 import com.google.common.collect.FluentIterable
12 import com.google.common.collect.HashMultimap
13 import com.google.common.util.concurrent.SettableFuture
14 import java.util.AbstractMap.SimpleEntry
15 import java.util.ArrayList
16 import java.util.Collections
18 import java.util.Map.Entry
20 import java.util.concurrent.Callable
21 import java.util.concurrent.ConcurrentHashMap
22 import java.util.concurrent.ConcurrentMap
23 import java.util.concurrent.Future
24 import javassist.ClassPool
25 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
26 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
27 import org.opendaylight.yangtools.binding.generator.util.Types
28 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy
29 import org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils
30 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
31 import org.opendaylight.yangtools.sal.binding.model.api.Type
32 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
33 import org.opendaylight.yangtools.yang.binding.Augmentation
34 import org.opendaylight.yangtools.yang.binding.BindingMapping
35 import org.opendaylight.yangtools.yang.binding.DataContainer
36 import org.opendaylight.yangtools.yang.binding.DataObject
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
38 import org.opendaylight.yangtools.yang.binding.RpcService
39 import org.opendaylight.yangtools.yang.common.QName
40 import org.opendaylight.yangtools.yang.data.api.CompositeNode
41 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
42 import org.opendaylight.yangtools.yang.data.api.Node
43 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
44 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
45 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec
46 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService
47 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec
48 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException
49 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName
50 import org.opendaylight.yangtools.yang.model.api.Module
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext
52 import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder
53 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener
54 import org.opendaylight.yangtools.yang.model.api.SchemaNode
55 import org.opendaylight.yangtools.yang.model.api.SchemaPath
56 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
57 import org.slf4j.LoggerFactory
59 class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
60 SchemaLock, AutoCloseable, SchemaContextHolder {
65 private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl);
68 extension TransformerGenerator binding;
71 extension LazyGeneratedCodecRegistry registry;
74 val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
77 val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
80 val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
83 val ConcurrentMap<Type, Set<QName>> serviceTypeToRpc = new ConcurrentHashMap();
85 val promisedTypes = HashMultimap.<Type, SettableFuture<Type>>create;
87 val ClassLoadingStrategy classLoadingStrategy;
90 var SchemaContext schemaContext;
93 this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy())
96 new(ClassLoadingStrategy strat){
97 classLoadingStrategy = strat
100 //ServiceRegistration<SchemaServiceListener> listenerRegistration
101 override onGlobalContextUpdated(SchemaContext arg0) {
103 recreateBindingContext(arg0);
104 registry.onGlobalContextUpdated(arg0);
107 def recreateBindingContext(SchemaContext schemaContext) {
108 val newBinding = new BindingGeneratorImpl();
109 newBinding.generateTypes(schemaContext);
111 for (entry : newBinding.moduleContexts.entrySet) {
113 registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
114 binding.pathToType.putAll(entry.value.childNodes)
115 val module = entry.key;
116 val context = entry.value;
117 updateBindingFor(context.childNodes, schemaContext);
118 updateBindingFor(context.cases, schemaContext);
119 val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
121 if (!module.rpcs.empty) {
122 val rpcs = FluentIterable.from(module.rpcs).transform[QName].toSet
123 val serviceClass = new ReferencedTypeImpl(namespace,
124 BindingGeneratorUtil.parseToClassName(module.name) + "Service");
125 serviceTypeToRpc.put(serviceClass, rpcs);
128 val typedefs = context.typedefs;
129 for (typedef : typedefs.entrySet) {
130 val typeRef = new ReferencedTypeImpl(typedef.value.packageName, typedef.value.name)
131 binding.typeDefinitions.put(typeRef, typedef.value as GeneratedType);
132 val schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.key);
133 if (schemaNode != null) {
135 binding.typeToSchemaNode.put(typeRef, schemaNode);
137 LOG.error("Type definition for {} is not available", typedef.value);
141 val augmentations = context.augmentations;
142 for (augmentation : augmentations) {
143 binding.typeToDefinition.put(augmentation, augmentation);
145 binding.typeToAugmentation.putAll(context.typeToAugmentation);
146 for (augmentation : augmentations) {
147 updatePromisedSchemas(augmentation);
152 override CompositeNode toDataDom(DataObject data) {
153 toCompositeNodeImpl(data);
156 override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
157 Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
160 val key = toDataDom(entry.key)
161 var CompositeNode data;
162 if (Augmentation.isAssignableFrom(entry.key.targetType)) {
163 data = toCompositeNodeImplAugument(key, entry.value);
165 data = toCompositeNodeImpl(key, entry.value);
167 return new SimpleEntry(key, data);
169 } catch (Exception e) {
170 LOG.error("Error during serialization for {}.", entry.key, e);
175 private def CompositeNode toCompositeNodeImpl(DataObject object) {
176 val cls = object.implementedInterface;
178 val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
179 val ret = codec.serialize(new ValueWithQName(null, object));
180 return ret as CompositeNode;
183 private def CompositeNode toCompositeNodeImpl(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,
185 val last = identifier.path.last;
186 val cls = object.implementedInterface;
188 val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
189 val ret = codec.serialize(new ValueWithQName(last.nodeType, object));
190 return ret as CompositeNode;
193 private def CompositeNode toCompositeNodeImplAugument(
194 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier, DataObject object) {
196 //val cls = object.implementedInterface;
197 //waitForSchema(cls);
198 val last = identifier.path.last;
199 val codec = registry.getCodecForAugmentation(object.implementedInterface as Class) as AugmentationCodec;
200 val ret = codec.serialize(new ValueWithQName(last.nodeType, object));
201 if (last instanceof NodeIdentifierWithPredicates) {
202 val predicates = last as NodeIdentifierWithPredicates;
203 val newNodes = new ArrayList<Node<?>>(predicates.keyValues.size);
204 for (predicate : predicates.keyValues.entrySet) {
205 newNodes.add(new SimpleNodeTOImpl(predicate.key, null, predicate.value));
207 newNodes.addAll(ret.children);
208 return new CompositeNodeTOImpl(last.nodeType, null, newNodes);
210 return ret as CompositeNode;
213 override waitForSchema(Class class1) {
215 if (registry.isCodecAvailable(class1)) {
218 val ref = Types.typeForClass(class1);
219 getSchemaWithRetry(ref);
222 override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
223 InstanceIdentifier<? extends DataObject> path) {
224 for (arg : path.path) {
225 waitForSchema(arg.type);
227 return registry.instanceIdentifierCodec.serialize(path);
230 override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
231 dataObjectFromDataDom(path.targetType, node) as DataObject;
234 override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) {
235 return tryDeserialization[ |
236 registry.instanceIdentifierCodec.deserialize(entry);
240 override getCodecRegistry() {
241 return getRegistry();
244 private static def <T> T tryDeserialization(Callable<T> deserializationBlock) throws DeserializationException {
246 deserializationBlock.call()
247 } catch (Exception e) {
249 // FIXME: Make this block providing more information.
250 throw new DeserializationException(e);
254 private def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
256 for (entry : map.entrySet) {
257 val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
259 //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
260 val typeRef = new ReferencedTypeImpl(entry.value.packageName, entry.value.name)
261 typeToDefinition.put(typeRef, entry.value);
262 if (schemaNode != null) {
263 typeToSchemaNode.put(typeRef, schemaNode);
264 updatePromisedSchemas(entry.value);
270 public def void init() {
271 binding = new TransformerGenerator(pool);
272 registry = new LazyGeneratedCodecRegistry(this, classLoadingStrategy)
274 registry.generator = binding
276 //binding.staticFieldsInitializer = registry
277 binding.listener = registry
278 binding.typeToDefinition = typeToDefinition
279 binding.typeToSchemaNode = typeToSchemaNode
280 binding.typeDefinitions = typeDefinitions
282 // if (ctx !== null) {
283 // listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable<String, String>());
287 override getRpcQNamesFor(Class<? extends RpcService> service) {
288 var serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.package.name, service.simpleName))
289 if (serviceRef == null) {
290 serviceRef = Collections.emptySet()
295 private def void getSchemaWithRetry(Type type) {
296 if (typeToDefinition.containsKey(type)) {
299 LOG.info("Thread blocked waiting for schema for: {}", type.fullyQualifiedName)
300 type.waitForTypeDefinition.get();
301 LOG.info("Schema for {} became available, thread unblocked", type.fullyQualifiedName)
304 private def Future<Type> waitForTypeDefinition(Type type) {
305 val future = SettableFuture.<Type>create()
306 promisedTypes.put(type, future);
310 private def void updatePromisedSchemas(Type builder) {
311 val ref = new ReferencedTypeImpl(builder.packageName, builder.name);
312 val futures = promisedTypes.get(ref);
313 if (futures === null || futures.empty) {
316 for (future : futures) {
319 promisedTypes.removeAll(builder);
322 override close() throws Exception {
323 //listenerRegistration?.unregister();
326 override dataObjectFromDataDom(Class<? extends DataContainer> container, CompositeNode domData) {
327 return tryDeserialization[ |
328 if (domData == null) {
331 val transformer = registry.getCodecForDataObject(container);
332 val ret = transformer.deserialize(domData)?.value as DataObject;
337 override getRpcServiceClassFor(String namespace, String revision) {
338 val module = schemaContext?.findModuleByName(namespace.toString, QName.parseRevision(revision));
339 if (module == null) {
340 return Optional.absent();
343 val rpcTypeName = module.rpcServiceType;
344 if (rpcTypeName.present) {
345 val rpcClass = binding.classLoadingStrategy.loadClass(rpcTypeName.get.fullyQualifiedName);
346 return Optional.of(rpcClass as Class<? extends RpcService>);
348 } catch (Exception e) {
349 LOG.debug("RPC class not present for {},{}", namespace, revision, e);
351 return Optional.absent()
354 def Optional<Type> getRpcServiceType(Module module) {
355 val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
356 if (module.rpcs.empty) {
357 return Optional.<Type>absent();
359 return Optional.<Type>of(
360 new ReferencedTypeImpl(namespace,
361 BindingMapping.getClassName(module.name) + BindingMapping.RPC_SERVICE_SUFFIX));