--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.restconf.server.mdsal;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
+import org.opendaylight.restconf.server.api.DatabindContext;
+import org.opendaylight.restconf.server.spi.DatabindProvider;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * A {@link DatabindProvider} tracking a {@link DOMSchemaService}.
+ */
+@Singleton
+@Component(service = { DatabindProvider.class, MdsalDatabindProvider.class })
+public final class MdsalDatabindProvider implements DatabindProvider, AutoCloseable {
+ private static final VarHandle CURRENT_DATABIND;
+
+ static {
+ try {
+ CURRENT_DATABIND = MethodHandles.lookup()
+ .findVarHandle(MdsalDatabindProvider.class, "currentDatabind", DatabindContext.class);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private final @Nullable YangTextSourceExtension sourceProvider;
+ private final Registration reg;
+
+ @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749")
+ private volatile DatabindContext currentDatabind;
+
+ @Inject
+ @Activate
+ public MdsalDatabindProvider(@Reference final DOMSchemaService schemaService) {
+ sourceProvider = schemaService.extension(YangTextSourceExtension.class);
+ currentDatabind = DatabindContext.ofModel(schemaService.getGlobalContext());
+ reg = schemaService.registerSchemaContextListener(this::onModelContextUpdated);
+ }
+
+ @PreDestroy
+ @Deactivate
+ @Override
+ public void close() {
+ reg.close();
+ }
+
+ @Override
+ public DatabindContext currentDatabind() {
+ return verifyNotNull((@NonNull DatabindContext) CURRENT_DATABIND.getAcquire(this));
+ }
+
+ @Nullable YangTextSourceExtension sourceProvider() {
+ return sourceProvider;
+ }
+
+ private void onModelContextUpdated(final EffectiveModelContext newModelContext) {
+ final var local = currentDatabind;
+ if (!newModelContext.equals(local.modelContext())) {
+ CURRENT_DATABIND.setRelease(this, DatabindContext.ofModel(newModelContext));
+ }
+ }
+}
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfFuture;
import org.opendaylight.restconf.server.api.PatchBody;
import org.opendaylight.restconf.server.api.ResourceBody;
import org.opendaylight.restconf.server.api.RestconfServer;
-import org.opendaylight.restconf.server.spi.DatabindProvider;
import org.opendaylight.restconf.server.spi.RpcImplementation;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.YangApi;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.restconf.Restconf;
-import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.YangNames;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
/**
* A RESTCONF server implemented on top of MD-SAL.
*/
@Singleton
-@Component(service = { RestconfServer.class, DatabindProvider.class })
-public final class MdsalRestconfServer implements RestconfServer, DatabindProvider, AutoCloseable {
+@Component(service = RestconfServer.class)
+public final class MdsalRestconfServer implements RestconfServer, AutoCloseable {
private static final QName YANG_LIBRARY_VERSION = QName.create(Restconf.QNAME, "yang-library-version").intern();
private static final VarHandle LOCAL_STRATEGY;
private final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
private final @NonNull DOMMountPointService mountPointService;
+ private final @NonNull MdsalDatabindProvider databindProvider;
private final @NonNull DOMDataBroker dataBroker;
private final @Nullable DOMRpcService rpcService;
private final @Nullable DOMActionService actionService;
- private final @Nullable YangTextSourceExtension sourceProvider;
- private final Registration reg;
-
- @SuppressWarnings("unused")
@SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749")
private volatile MdsalRestconfStrategy localStrategy;
@Inject
@Activate
- public MdsalRestconfServer(@Reference final DOMSchemaService schemaService,
+ public MdsalRestconfServer(@Reference final MdsalDatabindProvider databindProvider,
@Reference final DOMDataBroker dataBroker, @Reference final DOMRpcService rpcService,
@Reference final DOMActionService actionService,
@Reference final DOMMountPointService mountPointService,
- @Reference final List<RpcImplementation> localRpcs) {
+ @Reference(policyOption = ReferencePolicyOption.GREEDY) final List<RpcImplementation> localRpcs) {
+ this.databindProvider = requireNonNull(databindProvider);
this.dataBroker = requireNonNull(dataBroker);
this.rpcService = requireNonNull(rpcService);
this.actionService = requireNonNull(actionService);
this.mountPointService = requireNonNull(mountPointService);
- this.localRpcs = Maps.uniqueIndex(localRpcs, RpcImplementation::qname);
- sourceProvider = schemaService.extension(YangTextSourceExtension.class);
- localStrategy = createLocalStrategy(schemaService.getGlobalContext());
- reg = schemaService.registerSchemaContextListener(this::onModelContextUpdated);
+ this.localRpcs = Maps.uniqueIndex(localRpcs, RpcImplementation::qname);
+ localStrategy = createLocalStrategy(databindProvider.currentDatabind());
}
- public MdsalRestconfServer(final DOMSchemaService schemaService, final DOMDataBroker dataBroker,
+ public MdsalRestconfServer(final MdsalDatabindProvider databindProvider, final DOMDataBroker dataBroker,
final DOMRpcService rpcService, final DOMActionService actionService,
final DOMMountPointService mountPointService, final RpcImplementation... localRpcs) {
- this(schemaService, dataBroker, rpcService, actionService, mountPointService, List.of(localRpcs));
+ this(databindProvider, dataBroker, rpcService, actionService, mountPointService, List.of(localRpcs));
}
- @Override
- public DatabindContext currentDatabind() {
- return localStrategy().databind();
+ private @NonNull MdsalRestconfStrategy createLocalStrategy(final DatabindContext databind) {
+ return new MdsalRestconfStrategy(databind, dataBroker, rpcService, actionService,
+ databindProvider.sourceProvider(), mountPointService, localRpcs);
}
- private void onModelContextUpdated(final EffectiveModelContext newModelContext) {
- final var local = localStrategy();
- if (!newModelContext.equals(local.modelContext())) {
- LOCAL_STRATEGY.setRelease(this, createLocalStrategy(newModelContext));
- }
- }
-
- private @NonNull MdsalRestconfStrategy createLocalStrategy(final EffectiveModelContext modelContext) {
- return new MdsalRestconfStrategy(DatabindContext.ofModel(modelContext), dataBroker, rpcService, actionService,
- sourceProvider, mountPointService, localRpcs);
+ private @NonNull MdsalRestconfStrategy localStrategy() {
+ final var strategy = verifyNotNull((@NonNull MdsalRestconfStrategy) LOCAL_STRATEGY.getAcquire(this));
+ final var databind = databindProvider.currentDatabind();
+ return databind.equals(strategy.databind()) ? strategy : updateLocalStrategy(databind);
}
- private @NonNull MdsalRestconfStrategy localStrategy() {
- return verifyNotNull((MdsalRestconfStrategy) LOCAL_STRATEGY.getAcquire(this));
+ private @NonNull MdsalRestconfStrategy updateLocalStrategy(final DatabindContext databind) {
+ final var strategy = createLocalStrategy(databind);
+ localStrategy = strategy;
+ return strategy;
}
@PreDestroy
@Deactivate
@Override
public void close() {
- reg.close();
localStrategy = null;
}
* RESTCONF implementation of {@link CreateDataChangeEventSubscription}.
*/
@Singleton
-@Component
+@Component(service = RpcImplementation.class)
public final class CreateDataChangeEventSubscriptionRpc extends RpcImplementation {
private static final @NonNull NodeIdentifier DATASTORE_NODEID = NodeIdentifier.create(
QName.create(CreateDataChangeEventSubscriptionInput1.QNAME, "datastore").intern());
* RESTCONF implementation of {@link CreateNotificationStream}.
*/
@Singleton
-@Component
+@Component(service = RpcImplementation.class)
public final class CreateNotificationStreamRpc extends RpcImplementation {
private static final NodeIdentifier SAL_REMOTE_OUTPUT_NODEID =
NodeIdentifier.create(CreateDataChangeEventSubscriptionOutput.QNAME);
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.JsonNormalizedNodeBodyWriter;
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.XmlNormalizedNodeBodyWriter;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
+import org.opendaylight.restconf.server.mdsal.MdsalDatabindProvider;
import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@BeforeEach
final void setupRestconf() {
- restconf = new JaxRsRestconf(new MdsalRestconfServer(new FixedDOMSchemaService(modelContext()), dataBroker,
- rpcService, actionService, mountPointService));
+ restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ new MdsalDatabindProvider(new FixedDOMSchemaService(modelContext())), dataBroker, rpcService, actionService,
+ mountPointService));
}
EffectiveModelContext modelContext() {
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
+import org.opendaylight.restconf.server.mdsal.MdsalDatabindProvider;
import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
.build())))
.when(actionService).invokeAction(eq(RESET_PATH), any(), any());
- final var restconf = new JaxRsRestconf(new MdsalRestconfServer(new FixedDOMSchemaService(IID_SCHEMA),
- dataBroker, rpcService, actionService, mountPointService));
+ final var restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ new MdsalDatabindProvider(new FixedDOMSchemaService(IID_SCHEMA)), dataBroker, rpcService, actionService,
+ mountPointService));
doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
doReturn(true).when(asyncResponse).resume(captor.capture());
restconf.postDataJSON(ApiPath.parse("instance-identifier-module:cont/cont1/reset"),
.build())))
.when(actionService).invokeAction(eq(RESET_PATH), any(), any());
- final var restconf = new JaxRsRestconf(new MdsalRestconfServer(new FixedDOMSchemaService(IID_SCHEMA),
- dataBroker, rpcService, actionService, mountPointService));
+ final var restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ new MdsalDatabindProvider(new FixedDOMSchemaService(IID_SCHEMA)), dataBroker, rpcService, actionService,
+ mountPointService));
doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
doReturn(true).when(asyncResponse).resume(captor.capture());
restconf.postDataJSON(ApiPath.parse("instance-identifier-module:cont/cont1/reset"),
package org.opendaylight.restconf.nb.jaxrs;
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.doReturn;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertEntity;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertError;
import com.google.common.io.CharStreams;
import java.io.Reader;
-import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
import org.opendaylight.mdsal.dom.broker.DOMMountPointServiceImpl;
import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
+import org.opendaylight.restconf.server.mdsal.MdsalDatabindProvider;
import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
// handlers
@Mock
- private DOMSchemaService schemaService;
- @Mock
- private YangTextSourceExtension sourceProvider;
- @Mock
private DOMDataBroker dataBroker;
@Mock
private DOMActionService actionService;
.createMountPoint(YangInstanceIdentifier.of(QName.create("mount:point:2", "2016-01-01", "cont")))
.register();
- doCallRealMethod().when(schemaService).extension(any());
- doReturn(List.of(sourceProvider)).when(schemaService).supportedExtensions();
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
-
- restconf = new JaxRsRestconf(new MdsalRestconfServer(schemaService, dataBroker, rpcService, actionService,
- mountPointService));
+ restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ new MdsalDatabindProvider(new FixedDOMSchemaService(SCHEMA_CONTEXT_WITH_MOUNT_POINTS)), dataBroker,
+ rpcService, actionService, mountPointService));
}
/**
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertEntity;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertError;
import com.google.common.util.concurrent.Futures;
import java.io.IOException;
import java.io.Reader;
-import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService;
import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
+import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
+import org.opendaylight.restconf.server.mdsal.MdsalDatabindProvider;
import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class RestconfSchemaServiceTest {
// schema context with modules
- private static final EffectiveModelContext SCHEMA_CONTEXT =
+ private static final EffectiveModelContext MODEL_CONTEXT =
YangParserTestUtils.parseYangResourceDirectory("/modules");
- @Mock
- private DOMSchemaService schemaService;
@Mock
private YangTextSourceExtension sourceProvider;
@Mock
@Before
public void setup() throws Exception {
- doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
- doCallRealMethod().when(schemaService).extension(any());
- doReturn(List.of(sourceProvider)).when(schemaService).supportedExtensions();
-
- restconf = new JaxRsRestconf(new MdsalRestconfServer(schemaService, dataBroker, rpcService, actionService,
- mountPointService));
+ restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ new MdsalDatabindProvider(new FixedDOMSchemaService(() -> MODEL_CONTEXT, sourceProvider)), dataBroker,
+ rpcService, actionService, mountPointService));
}
/**