/*
- * Copyright (c) 2014, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2014 - 2016 Brocade Communication Systems, Inc., 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,
public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
boolean startTime_used = false;
boolean stopTime_used = false;
- boolean filter_used = false;
Instant start = Instant.now();
Instant stop = null;
+ boolean filter_used = false;
String filter = null;
+ boolean leafNodesOnly_used = false;
+ boolean leafNodesOnly = false;
for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
switch (entry.getKey()) {
throw new RestconfDocumentedException("Filter parameter can be used only once.");
}
break;
+ case "odl-leaf-nodes-only":
+ if (!leafNodesOnly_used) {
+ leafNodesOnly_used = true;
+ leafNodesOnly = Boolean.parseBoolean(entry.getValue().iterator().next());
+ } else {
+ throw new RestconfDocumentedException("Odl-leaf-nodes-only parameter can be used only once.");
+ }
+ break;
default:
throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey());
}
}
URI response = null;
if (identifier.contains(DATA_SUBSCR)) {
- response = dataSubs(identifier, uriInfo, start, stop, filter);
+ response = dataSubs(identifier, uriInfo, start, stop, filter, leafNodesOnly);
} else if (identifier.contains(NOTIFICATION_STREAM)) {
response = notifStream(identifier, uriInfo, start, stop, filter);
}
for (final NotificationListenerAdapter listener : listeners) {
this.broker.registerToListenNotification(listener);
- listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter));
+ listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter), false);
}
final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
* @return {@link URI} of location
*/
private URI dataSubs(final String identifier, final UriInfo uriInfo, final Instant start, final Instant stop,
- final String filter) {
+ final String filter, boolean leafNodesOnly) {
final String streamName = Notificator.createStreamNameFromUri(identifier);
if (Strings.isNullOrEmpty(streamName)) {
throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
ErrorTag.UNKNOWN_ELEMENT);
}
- listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter));
+ listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter), leafNodesOnly);
final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
final LogicalDatastoreType datastore =
private Instant start = null;
private Instant stop = null;
private String filter = null;
+ private boolean leafNodesOnly = false;
@VisibleForTesting
public final Instant getStart() {
* - stop-time of getting notification
* @param filter
* - indicate which subset of all possible events are of interest
+ * @param leafNodesOnly
+ * - if true, notifications will contain changes to leaf nodes only
*/
- public void setQueryParams(final Instant start, final Optional<Instant> stop, final Optional<String> filter) {
+ public void setQueryParams(final Instant start, final Optional<Instant> stop, final Optional<String> filter, final boolean leafNodesOnly) {
this.start = Preconditions.checkNotNull(start);
this.stop = stop.orElse(null);
this.filter = filter.orElse(null);
+ this.leafNodesOnly = leafNodesOnly;
+ }
+
+ /**
+ * Check whether this query should only notify about leaf node changes
+ *
+ * @return true if this query should only notify about leaf node changes
+ */
+ public boolean getLeafNodesOnly() {
+ return leafNodesOnly;
}
/**
/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2014, 2016 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,
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.restconf.parser.builder.YangInstanceIdentifierDeserializer;
import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
return;
}
for (final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry : data) {
- if (!ControllerContext.getInstance().isNodeMixin(entry.getKey())) {
+ if (!ControllerContext.getInstance().isNodeMixin(entry.getKey()) &&
+ (!getLeafNodesOnly() || entry.getValue() instanceof LeafNode)) {
final Node node = createCreatedChangedDataChangeEventElement(doc, entry, operation, schemaContext,
dataSchemaContextTree);
element.appendChild(node);
for (final NotificationListenerAdapter listener : listeners) {
registerToListenNotification(listener, handlersHolder.getNotificationServiceHandler());
listener.setQueryParams(notificationQueryParams.getStart(), notificationQueryParams.getStop(),
- notificationQueryParams.getFilter());
+ notificationQueryParams.getFilter(), false);
listener.setCloseVars(handlersHolder.getTransactionChainHandler(), handlersHolder.getSchemaHandler());
final NormalizedNode mapToStreams =
RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(listener.getSchemaPath().getLastComponent(),
Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName);
listener.setQueryParams(notificationQueryParams.getStart(), notificationQueryParams.getStop(),
- notificationQueryParams.getFilter());
+ notificationQueryParams.getFilter(), false);
listener.setCloseVars(handlersHolder.getTransactionChainHandler(), handlersHolder.getSchemaHandler());
registration(ds, scope, listener, handlersHolder.getDomDataBrokerHandler().get());
final PathArgument pathValue = NodeIdentifier.create(QName.create("module", "2016-14-12", "localName"));
Mockito.when(path.getLastPathArgument()).thenReturn(pathValue);
final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON);
- listener.setQueryParams(Instant.now(), Optional.empty(), Optional.ofNullable(filter));
+ listener.setQueryParams(Instant.now(), Optional.empty(), Optional.ofNullable(filter), false);
// FIXME: do not use reflection here
final Class<?> superclass = listener.getClass().getSuperclass().getSuperclass();
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
+import java.util.logging.Level;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
-public class RestStream extends JerseyTest {
+public class RestStreamTest extends JerseyTest {
private static BrokerFacade brokerFacade;
private static RestconfImpl restconfImpl;
}
@Test
- @Ignore // FIXME : find problem with codec
+ @Ignore // Sporadic failures where jersey does not correctly pass post data to XmlNormalizedNodeBodyReader.readFrom
public void testCallRpcCallGet() throws UnsupportedEncodingException, InterruptedException {
+ createAndSubscribe(null);
+ }
+
+ @Test
+ @Ignore // Sporadic failures where jersey does not correctly pass post data to XmlNormalizedNodeBodyReader.readFrom
+ public void testCallRpcCallGetLeaves() throws UnsupportedEncodingException, InterruptedException {
+ createAndSubscribe("odl-leaf-nodes-only", "true");
+ }
+
+ private void createAndSubscribe(String queryParamName, Object... values)
+ throws UnsupportedEncodingException, InterruptedException {
String uri = "/operations/sal-remote:create-data-change-event-subscription";
- final Response responseWithStreamName = post(uri, MediaType.APPLICATION_XML, getRpcInput());
+ String rpcInput = getRpcInput();
+ final Response responseWithStreamName = post(uri, MediaType.APPLICATION_XML, rpcInput);
final Document xmlResponse = responseWithStreamName.readEntity(Document.class);
assertNotNull(xmlResponse);
final Element outputElement = xmlResponse.getDocumentElement();
final Node streamNameElement = outputElement.getFirstChild();
assertEquals("stream-name",streamNameElement.getLocalName());
- assertEquals("ietf-interfaces:interfaces/ietf-interfaces:interface/eth0/datastore=CONFIGURATION/scope=BASE",streamNameElement.getTextContent());
+ assertEquals("data-change-event-subscription/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0/datastore=CONFIGURATION/scope=BASE",streamNameElement.getTextContent());
- uri = "/streams/stream/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0/datastore=CONFIGURATION/scope=BASE";
- final Response responseWithRedirectionUri = get(uri, MediaType.APPLICATION_XML);
+ uri = "/streams/stream/data-change-event-subscription/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0/datastore=CONFIGURATION/scope=BASE";
+ final Response responseWithRedirectionUri = get(uri, MediaType.APPLICATION_XML, null);
final URI websocketServerUri = responseWithRedirectionUri.getLocation();
assertNotNull(websocketServerUri);
- assertTrue(websocketServerUri.toString().matches(".*http://localhost:[\\d]+/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0.*"));
+ assertTrue(websocketServerUri.toString().matches(".*ws://localhost:[\\d]+/data-change-event-subscription/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0.*"));
}
private Response post(final String uri, final String mediaType, final String data) {
return target(uri).request(mediaType).post(Entity.entity(data, mediaType));
}
- private Response get(final String uri, final String mediaType) {
- return target(uri).request(mediaType).get();
+ private Response get(final String uri, final String mediaType, String queryParam, Object... values) {
+ if (queryParam != null) {
+ return target(uri).queryParam(queryParam, values).request(mediaType).get();
+ } else {
+ return target(uri).request(mediaType).get();
+ }
}
private static String getRpcInput() {