+ private final LoadingCache<Class<?>, NotificationCodecContext<?>> notificationsByClass = CacheBuilder.newBuilder()
+ .build(new CacheLoader<Class<?>, NotificationCodecContext<?>>() {
+ @Override
+ public NotificationCodecContext<?> load(final Class<?> key) {
+ checkArgument(key.isInterface(), "Supplied class must be interface.");
+
+ // TODO: we should be able to work with bindingChild() instead of schemaTreeChild() here
+ final QName qname = BindingReflections.findQName(key);
+ final RuntimeType child = getType().schemaTreeChild(qname);
+ checkArgument(child instanceof NotificationRuntimeType, "Supplied %s is not valid notification",
+ key);
+ return new NotificationCodecContext<>(key, (NotificationRuntimeType) child, factory());
+ }
+ });
+
+ private final LoadingCache<Class<?>, ContainerNodeCodecContext<?>> rpcDataByClass = CacheBuilder.newBuilder()
+ .build(new CacheLoader<Class<?>, ContainerNodeCodecContext<?>>() {
+ @Override
+ public ContainerNodeCodecContext<?> load(final Class<?> key) {
+ final BiFunction<BindingRuntimeTypes, QName, Optional<? extends ContainerLikeRuntimeType<?, ?>>> lookup;
+ if (RpcInput.class.isAssignableFrom(key)) {
+ lookup = BindingRuntimeTypes::findRpcInput;
+ } else if (RpcOutput.class.isAssignableFrom(key)) {
+ lookup = BindingRuntimeTypes::findRpcOutput;
+ } else {
+ throw new IllegalArgumentException(key + " does not represent an RPC container");
+ }
+
+ final CodecContextFactory factory = factory();
+ final BindingRuntimeContext context = factory.getRuntimeContext();
+
+ final QName qname = BindingReflections.findQName(key);
+ final QNameModule qnameModule = qname.getModule();
+ final Module module = context.getEffectiveModelContext().findModule(qnameModule)
+ .orElseThrow(() -> new IllegalArgumentException("Failed to find module for " + qnameModule));
+ final String className = BindingMapping.getClassName(qname);
+
+ for (final RpcDefinition potential : module.getRpcs()) {
+ final QName potentialQName = potential.getQName();
+ /*
+ * Check if rpc and class represents data from same module and then checks if rpc local name
+ * produces same class name as class name appended with Input/Output based on QName associated
+ * with binding class.
+ *
+ * FIXME: Rework this to have more precise logic regarding Binding Specification.
+ */
+ if (key.getSimpleName().equals(BindingMapping.getClassName(potentialQName) + className)) {
+ final ContainerLike schema = getRpcDataSchema(potential, qname);
+ checkArgument(schema != null, "Schema for %s does not define input / output.", potentialQName);
+
+ final ContainerLikeRuntimeType<?, ?> type = lookup.apply(context.getTypes(), potentialQName)
+ .orElseThrow(() -> new IllegalArgumentException("Cannot find runtime type for " + key));
+
+ return (ContainerNodeCodecContext) DataContainerCodecPrototype.from(key,
+ (ContainerLikeRuntimeType<?, ?>) type, factory).get();
+ }
+ }
+
+ throw new IllegalArgumentException("Supplied class " + key + " is not valid RPC class.");
+ }
+ });
+