*/
package org.opendaylight.yangtools.sal.binding.generator.impl;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.net.URI;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.Callable;
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.eclipse.xtext.xbase.lib.Extension;
+import javax.annotation.concurrent.GuardedBy;
+
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.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.AugmentationSchema;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Objects;
-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 {
+SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
private static final Logger LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl.class);
- private final ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap<>();
- private final ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap<>();
- private final ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap<>();
private final ConcurrentMap<Type, Set<QName>> serviceTypeToRpc = new ConcurrentHashMap<>();
- private final HashMultimap<Type, SettableFuture<Type>> promisedTypes = HashMultimap.create();
- private final ClassLoadingStrategy classLoadingStrategy;
- // FIXME: how is this thread-safe?
- private ClassPool pool;
+ /**
+ * This is map of types which users are waiting for.
+ */
+ @GuardedBy("this")
+ private final Multimap<Type, SettableFuture<Type>> promisedTypes = HashMultimap.create();
- // FIXME: how is this thread-safe?
- @Extension
- private TransformerGenerator binding;
+ private final ClassLoadingStrategy classLoadingStrategy;
- // FIXME: how is this thread-safe?
- @Extension
- private LazyGeneratedCodecRegistry registry;
+ private final AbstractTransformerGenerator binding;
+ private final LazyGeneratedCodecRegistry registry;
+ private final ClassPool pool;
- // FIXME: how is this thread-safe?
- private SchemaContext schemaContext;
+ /*
+ * FIXME: updated here, access from AbstractTransformer
+ */
+ private final Map<Type, AugmentationSchema> typeToAugmentation = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap<>();
+ private final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
- public RuntimeGeneratedMappingServiceImpl() {
- this(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
- }
+ // FIXME: need to figure these out
+ private final ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap<>();
+ private SchemaContext schemaContext;
- public RuntimeGeneratedMappingServiceImpl(final ClassLoadingStrategy strat) {
- classLoadingStrategy = strat;
+ public RuntimeGeneratedMappingServiceImpl(final ClassPool pool) {
+ this(pool, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
}
- public ClassPool getPool() {
- return this.pool;
- }
+ public RuntimeGeneratedMappingServiceImpl(final ClassPool pool, final ClassLoadingStrategy strat) {
+ this.pool = Preconditions.checkNotNull(pool);
+ this.classLoadingStrategy = Preconditions.checkNotNull(strat);
+ // FIXME: this escapes constructor
+ binding = new TransformerGenerator(this, pool);
+ registry = new LazyGeneratedCodecRegistry(this, binding, classLoadingStrategy);
+ binding.setListener(registry);
- public void setPool(final ClassPool pool) {
- this.pool = pool;
+ // if (ctx !== null) {
+ // listenerRegistration = ctx.registerService(SchemaServiceListener,
+ // this, new Hashtable<String, String>());
+ // }
}
@Override
- public SchemaContext getSchemaContext() {
+ public synchronized SchemaContext getSchemaContext() {
return schemaContext;
}
- public void setSchemaContext(final SchemaContext schemaContext) {
- this.schemaContext = schemaContext;
- }
-
- public TransformerGenerator getBinding() {
- return this.binding;
- }
-
- public void setBinding(final TransformerGenerator binding) {
- this.binding = binding;
- }
-
- public LazyGeneratedCodecRegistry getRegistry() {
- return registry;
- }
-
- public void setRegistry(final LazyGeneratedCodecRegistry registry) {
- this.registry = registry;
- }
-
- public ConcurrentMap<Type, GeneratedTypeBuilder> getTypeToDefinition() {
- return typeToDefinition;
- }
-
- public ConcurrentMap<Type, Type> getTypeDefinitions() {
- return typeDefinitions;
- }
-
- public ConcurrentMap<Type, SchemaNode> getTypeToSchemaNode() {
- return typeToSchemaNode;
- }
-
- public ConcurrentMap<Type, Set<QName>> getServiceTypeToRpc() {
- return serviceTypeToRpc;
- }
-
@Override
- public void onGlobalContextUpdated(final SchemaContext arg0) {
- this.setSchemaContext(arg0);
- this.recreateBindingContext(arg0);
- LazyGeneratedCodecRegistry _registry = this.getRegistry();
- _registry.onGlobalContextUpdated(arg0);
+ public synchronized void onGlobalContextUpdated(final SchemaContext context) {
+ this.schemaContext = Preconditions.checkNotNull(context);
+ this.recreateBindingContext(context);
+ this.registry.onGlobalContextUpdated(context);
}
+ @GuardedBy("this")
private void recreateBindingContext(final SchemaContext schemaContext) {
BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
newBinding.generateTypes(schemaContext);
for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
registry.onModuleContextAdded(schemaContext, entry.getKey(), entry.getValue());
- binding.getPathToType().putAll(entry.getValue().getChildNodes());
+ pathToType.putAll(entry.getValue().getChildNodes());
Module module = entry.getKey();
ModuleContext context = entry.getValue();
updateBindingFor(context.getChildNodes(), schemaContext);
for (Map.Entry<SchemaPath, Type> typedef : typedefs.entrySet()) {
Type value = typedef.getValue();
Type typeRef = new ReferencedTypeImpl(value.getPackageName(), value.getName());
- binding.getTypeDefinitions().put(typeRef, value);
+ typeDefinitions.put(typeRef, value);
TypeDefinition<?> schemaNode = YangSchemaUtils.findTypeDefinition(schemaContext, typedef.getKey());
if (schemaNode != null) {
- binding.getTypeToSchemaNode().put(typeRef, schemaNode);
+ typeToSchemaNode.put(typeRef, schemaNode);
} else {
LOG.error("Type definition for {} is not available", value);
}
}
List<GeneratedTypeBuilder> augmentations = context.getAugmentations();
for (GeneratedTypeBuilder augmentation : augmentations) {
- binding.getTypeToDefinition().put(augmentation, augmentation);
+ typeToDefinition.put(augmentation, augmentation);
}
- binding.getTypeToAugmentation().putAll(context.getTypeToAugmentation());
+ typeToAugmentation.putAll(context.getTypeToAugmentation());
for (GeneratedTypeBuilder augmentation : augmentations) {
updatePromisedSchemas(augmentation);
}
private CompositeNode toCompositeNodeImpl(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier identifier,
final DataObject object) {
- PathArgument last = identifier.getPath().get(identifier.getPath().size() - 1);
+ PathArgument last = identifier.getLastPathArgument();
Class<? extends DataContainer> cls = object.getImplementedInterface();
waitForSchema(cls);
DataContainerCodec<DataObject> codec = (DataContainerCodec<DataObject>) registry.getCodecForDataObject(cls);
// val cls = object.implementedInterface;
// waitForSchema(cls);
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument last = identifier.getPath().get(
- identifier.getPath().size() - 1);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument last = identifier.getLastPathArgument();
AugmentationCodec codec = registry.getCodecForAugmentation((Class) object.getImplementedInterface());
CompositeNode ret = codec.serialize(new ValueWithQName<DataObject>(last.getNodeType(), object));
if (last instanceof NodeIdentifierWithPredicates) {
for (Map.Entry<QName, Object> predicate : predicates.getKeyValues().entrySet()) {
newNodes.add(new SimpleNodeTOImpl<Object>(predicate.getKey(), null, predicate.getValue()));
}
- newNodes.addAll(ret.getChildren());
+ newNodes.addAll(ret.getValue());
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);
+ public void waitForSchema(final Class<?> cls) {
+ final ListenableFuture<Type> f = getSchemaDefinition(cls);
+ if (f != null) {
+ try {
+ f.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.warn("Waiting for schema for class {} failed", cls, e);
+ throw new IllegalStateException(String.format("Failed to get schema for %s", cls), e);
+ }
+ LOG.info("Schema for {} became available, thread unblocked", cls);
}
}
for (final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
this.waitForSchema(arg.getType());
}
- return registry.getInstanceIdentifierCodec().serialize(path);
+
+ 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<? extends DataObject> path,
- final CompositeNode node) throws DeserializationException {
+ final CompositeNode domData) throws DeserializationException {
+ if (domData == null) {
+ return null;
+ }
- final Class<? extends DataContainer> container = path.getTargetType();
- final CompositeNode domData = node;
+ try {
+ final Class<? extends DataContainer> container = path.getTargetType();
+ // FIXME: deprecate use without iid
+ final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> wildcardedPath = createWildcarded(path);
- return tryDeserialization(new Callable<DataObject>() {
- @Override
- public DataObject call() {
- if (Objects.equal(domData, null)) {
- return null;
- }
- final DataContainerCodec<? extends DataContainer> transformer = getRegistry()
- .getCodecForDataObject(container);
- // TODO: deprecate use without iid
- org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> wildcardedPath = createWildcarded(path);
- ValueWithQName<? extends DataContainer> deserialize = transformer.deserialize(domData, wildcardedPath);
- DataContainer value = null;
- if (deserialize != null) {
- value = deserialize.getValue();
- }
- return ((DataObject) value);
+ final DataContainerCodec<? extends DataContainer> transformer = registry.getCodecForDataObject(container);
+ Preconditions.checkState(transformer != null, "Failed to find codec for type %s", container);
+
+ final ValueWithQName<? extends DataContainer> 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<? extends Object> fromDataDom(final InstanceIdentifier entry) throws DeserializationException {
- return tryDeserialization(new Callable<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends Object>>() {
- @Override
- public org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends Object> call() {
- return getRegistry().getInstanceIdentifierCodec().deserialize(entry);
- }
- });
+ 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.getRegistry();
- }
-
- private static <T> T tryDeserialization(final Callable<T> deserializationBlock) throws DeserializationException {
- try {
- return deserializationBlock.call();
- } catch (Exception e) {
- // FIXME: Make this block providing more information.
- throw new DeserializationException("Failed to run deserialization", e);
- }
+ return this.registry;
}
private void updateBindingFor(final Map<SchemaPath, GeneratedTypeBuilder> map, final SchemaContext module) {
}
}
- public void init() {
- 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<String, String>());
- // }
- }
@Override
public Set<QName> getRpcQNamesFor(final Class<? extends RpcService> service) {
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 ListenableFuture<Type> getSchemaDefinition(final Class<?> cls) {
+ final Type type = Types.typeForClass(cls);
+ synchronized (this) {
+ if (typeToDefinition.containsKey(type)) {
+ return null;
+ }
- private Future<Type> waitForTypeDefinition(final Type type) {
- final SettableFuture<Type> future = SettableFuture.<Type> create();
- promisedTypes.put(type, future);
- return future;
+ LOG.info("Thread is going to wait for schema for: {}", type.getFullyQualifiedName());
+ final SettableFuture<Type> f = SettableFuture.create();
+ promisedTypes.put(type, f);
+ return f;
+ }
}
+ @GuardedBy("this")
private void updatePromisedSchemas(final Type builder) {
- Type ref = new ReferencedTypeImpl(builder.getPackageName(), builder.getName());
- Set<SettableFuture<Type>> futures = promisedTypes.get(ref);
- if (futures == null || futures.isEmpty()) {
- return;
- }
- for (SettableFuture<Type> future : futures) {
- future.set(builder);
+ final Type ref = new ReferencedTypeImpl(builder.getPackageName(), builder.getName());
+ final Collection<SettableFuture<Type>> futures = promisedTypes.get(ref);
+
+ if (futures != null) {
+ for (SettableFuture<Type> future : futures) {
+ future.set(builder);
+ }
+ promisedTypes.removeAll(builder);
}
- promisedTypes.removeAll(builder);
}
@Override
}
@Override
- public Optional<Class<? extends RpcService>> getRpcServiceClassFor(final String namespace, final String revision) {
+ public synchronized Optional<Class<? extends RpcService>> getRpcServiceClassFor(final String namespace, final String revision) {
Module module = null;
if (schemaContext != null) {
- module = schemaContext.findModuleByName(namespace, QName.parseRevision(revision));
+ module = schemaContext.findModuleByNamespaceAndRevision(URI.create(namespace), QName.parseRevision(revision));
}
if (module == null) {
return Optional.absent();
try {
Optional<Type> rpcTypeName = getRpcServiceType(module);
if (rpcTypeName.isPresent()) {
- Class<?> rpcClass = binding.getClassLoadingStrategy().loadClass(
- rpcTypeName.get().getFullyQualifiedName());
+ Class<?> rpcClass = classLoadingStrategy.loadClass(rpcTypeName.get().getFullyQualifiedName());
return Optional.<Class<? extends RpcService>> of((Class<? extends RpcService>) rpcClass);
}
} catch (Exception e) {
return org.opendaylight.yangtools.yang.binding.InstanceIdentifier.create(wildcardedArgs);
}
+ @Override
+ public final AugmentationSchema getAugmentation(final Type type) {
+ return typeToAugmentation.get(type);
+ }
+
+ @Override
+ public final GeneratedTypeBuilder getDefinition(final Type type) {
+ return typeToDefinition.get(type);
+ }
+
+ @Override
+ public final SchemaNode getSchemaNode(final Type type) {
+ return typeToSchemaNode.get(type);
+ }
+
+ @Override
+ public final GeneratedTypeBuilder getTypeBuilder(final SchemaPath path) {
+ return pathToType.get(path);
+ }
}