package org.opendaylight.netconf.sal.rest.impl;
import com.google.common.base.Preconditions;
-import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
@Override
public Object getRoot() {
- return restconf.getRoot();
+ return this.restconf.getRoot();
}
@Override
public NormalizedNodeContext getModules(final UriInfo uriInfo) {
- return restconf.getModules(uriInfo);
+ return this.restconf.getModules(uriInfo);
}
@Override
public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
- return restconf.getModules(identifier, uriInfo);
+ return this.restconf.getModules(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
- return restconf.getModule(identifier, uriInfo);
+ return this.restconf.getModule(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
- return restconf.getOperations(uriInfo);
+ return this.restconf.getOperations(uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
- return restconf.getOperations(identifier, uriInfo);
+ return this.restconf.getOperations(identifier, uriInfo);
}
@Override
- public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.invokeRpc(identifier, payload, uriInfo);
+ public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.invokeRpc(identifier, payload, uriInfo);
}
@Override
@Deprecated
public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
- return restconf.invokeRpc(identifier, noPayload, uriInfo);
+ return this.restconf.invokeRpc(identifier, noPayload, uriInfo);
}
@Override
public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
- return restconf.readConfigurationData(identifier, uriInfo);
+ return this.restconf.readConfigurationData(identifier, uriInfo);
}
@Override
public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
- return restconf.readOperationalData(identifier, uriInfo);
+ return this.restconf.readOperationalData(identifier, uriInfo);
}
@Override
public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
- return restconf.updateConfigurationData(identifier, payload);
+ return this.restconf.updateConfigurationData(identifier, payload);
}
@Override
- public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.createConfigurationData(identifier, payload, uriInfo);
+ public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.createConfigurationData(identifier, payload, uriInfo);
}
@Override
public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.createConfigurationData(payload, uriInfo);
+ return this.restconf.createConfigurationData(payload, uriInfo);
}
@Override
public Response deleteConfigurationData(final String identifier) {
- return restconf.deleteConfigurationData(identifier);
+ return this.restconf.deleteConfigurationData(identifier);
}
@Override
public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
- return restconf.subscribeToStream(identifier, uriInfo);
+ return this.restconf.subscribeToStream(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
- return restconf.getAvailableStreams(uriInfo);
+ return this.restconf.getAvailableStreams(uriInfo);
}
@Override
- public PATCHStatusContext patchConfigurationData(final String identifier, PATCHContext payload, UriInfo uriInfo) {
- return restconf.patchConfigurationData(identifier, payload, uriInfo);
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.patchConfigurationData(identifier, payload, uriInfo);
}
@Override
public PATCHStatusContext patchConfigurationData(final PATCHContext context, final UriInfo uriInfo) {
- return restconf.patchConfigurationData(context, uriInfo);
+ return this.restconf.patchConfigurationData(context, uriInfo);
}
@Override
public SchemaExportContext getSchema(final String mountId) {
- return schema.getSchema(mountId);
+ return this.schema.getSchema(mountId);
}
}
import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+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.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private static final String SCOPE_PARAM_NAME = "scope";
+ private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
+
private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
static {
try {
final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
- NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
+ NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null),
+ NETCONF_BASE_PAYLOAD_NAME);
SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
eventSubscriptionAugRevision);
- SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
- QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+ SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(
+ QName.create(SAL_REMOTE_AUGMENT, "scope"),
+ QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+ QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
} catch (final ParseException e) {
final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date";
LOG.debug(errMsg);
throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
final Set<Module> modules = this.controllerContext.getAllModules(mountPoint);
final MapNode mountPointModulesMap = makeModuleMapNode(modules);
DOMMountPoint mountPoint = null;
final SchemaContext schemaContext;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
schemaContext = mountPoint.getSchemaContext();
final SchemaContext schemaContext = this.controllerContext.getGlobalSchema();
final Set<String> availableStreams = Notificator.getStreamNames();
final Module restconfModule = getRestconfModule();
- final DataSchemaNode streamSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
- Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+ final DataSchemaNode streamSchemaNode = this.controllerContext
+ .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
Set<Module> modules = null;
DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
modules = this.controllerContext.getAllModules(mountPoint);
} else {
- final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
+ final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to "
+ + "end with ";
LOG.debug(errMsg + ControllerContext.MOUNT + " for " + identifier);
- throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL,
+ ErrorTag.INVALID_VALUE);
}
return operationsFromModulesToNormalizedContext(modules, mountPoint);
final Set<Module> fakeModules = new HashSet<>();
fakeModules.add(fakeModule);
final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules);
- final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext = new InstanceIdentifierContext<>(
- null, fakeContSchNode, mountPoint, fakeSchemaCtx);
+ final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext =
+ new InstanceIdentifierContext<>(null, fakeContSchNode, mountPoint, fakeSchemaCtx);
return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
}
}
@Override
- public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+ public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
final CheckedFuture<DOMRpcResult, DOMRpcException> response;
throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
ErrorTag.INVALID_VALUE);
} else if (cause instanceof DOMRpcImplementationNotAvailableException) {
- throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
+ throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION,
+ ErrorTag.OPERATION_NOT_SUPPORTED);
}
- throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",cause);
+ throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+ cause);
} else {
- throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",e);
+ throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+ e);
}
} catch (final CancellationException e) {
final String errMsg = "The operation was cancelled while executing.";
}
}
- private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
+ private CheckedFuture<DOMRpcResult, DOMRpcException>
+ invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
final ContainerNode value = (ContainerNode) payload.getData();
final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
String streamName = (String) CREATE_DATA_SUBSCR;
+ NotificationOutputType outputType = null;
if (!pathIdentifier.isEmpty()) {
final String fullRestconfIdentifier = DATA_SUBSCR
+ this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
- LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
+ LogicalDatastoreType datastore =
+ parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
scope = scope == null ? DEFAULT_SCOPE : scope;
+ outputType = parseEnumTypeParameter(value, NotificationOutputType.class,
+ OUTPUT_TYPE_PARAM_NAME);
+ outputType = outputType == null ? NotificationOutputType.XML : outputType;
+
streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
+ "/scope=" + scope);
}
final QName outputQname = QName.create(rpcQName, "output");
final QName streamNameQname = QName.create(rpcQName, "stream-name");
- final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
- .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+ final ContainerNode output =
+ ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+ .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
if (!Notificator.existListenerFor(streamName)) {
- Notificator.createListener(pathIdentifier, streamName);
+ Notificator.createListener(pathIdentifier, streamName, outputType);
}
final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
if ( ! uriKeyValue.equals(dataKeyValue)) {
final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
- "' specified in the URI doesn't match the value '" + dataKeyValue + "' specified in the message body. ";
+ "' specified in the URI doesn't match the value '" + dataKeyValue
+ + "' specified in the message body. ";
throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
}
}
@Override
- public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+ public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
return createConfigurationData(payload, uriInfo);
}
}
}
- private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
+ private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint,
+ final YangInstanceIdentifier normalizedII) {
if(uriInfo == null) {
// This is null if invoked internally
return null;
}
@Override
- public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context,
+ final UriInfo uriInfo) {
if (context == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
return null;
}
final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
- ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
+ ((AugmentationNode) augNode.get())
+ .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
if (!enumNode.isPresent()) {
return null;
}
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import org.json.JSONObject;
+import org.json.XML;
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.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private Set<Channel> subscribers = new ConcurrentSet<>();
private final EventBus eventBus;
private final EventBusChangeRecorder eventBusChangeRecorder;
+ private final NotificationOutputType outputType;
/**
- * Creates new {@link ListenerAdapter} listener specified by path and stream name.
+ * Creates new {@link ListenerAdapter} listener specified by path and stream
+ * name.
*
* @param path
* Path to data in data store.
* @param streamName
* The name of the stream.
+ * @param outputType
+ * - type of output on notification (JSON, XML)
*/
- ListenerAdapter(final YangInstanceIdentifier path, final String streamName) {
+ ListenerAdapter(final YangInstanceIdentifier path, final String streamName,
+ final NotificationOutputType outputType) {
+ this.outputType = outputType;
Preconditions.checkNotNull(path);
Preconditions.checkArgument((streamName != null) && !streamName.isEmpty());
this.path = path;
|| !change.getRemovedPaths().isEmpty()) {
final String xml = prepareXmlFrom(change);
final Event event = new Event(EventType.NOTIFY);
- event.setData(xml);
+ if (this.outputType.equals(NotificationOutputType.JSON)) {
+ final JSONObject jsonObject = XML.toJSONObject(xml);
+ event.setData(jsonObject.toString());
+ } else {
+ event.setData(xml);
+ }
this.eventBus.post(event);
}
}
final Document doc = createDocument();
final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
"notification");
+
doc.appendChild(notificationElement);
final Element eventTimeElement = doc.createElement("eventTime");
final Element dataChangedNotificationEventElement = doc.createElementNS(
"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
+
addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change,
schemaContext, dataContextTree);
notificationElement.appendChild(dataChangedNotificationEventElement);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
- transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
+ transformer.transform(new DOMSource(doc),
+ new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
final byte[] charData = out.toByteArray();
return new String(charData, "UTF-8");
} catch (TransformerException | UnsupportedEncodingException e) {
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data, final Element element,
- final Operation operation) {
+ private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data,
+ final Element element, final Operation operation) {
if ((data == null) || data.isEmpty()) {
return;
}
* {@link Operation}
* @return {@link Node} node represented by changed event element.
*/
- private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, final Operation operation) {
+ private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path,
+ final Operation operation) {
final Element dataChangeEventElement = doc.createElement("data-change-event");
final Element pathElement = doc.createElement("path");
addPathAsValueToElement(path, pathElement);
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public class Notificator {
- private static Map<String, ListenerAdapter> listenersByStreamName = new ConcurrentHashMap<>();
- private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName = new ConcurrentHashMap<>();
+ private static Map<String, ListenerAdapter> dataChangeListener = new ConcurrentHashMap<>();
+ private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName =
+ new ConcurrentHashMap<>();
private static final Logger LOG = LoggerFactory.getLogger(Notificator.class);
private static final Lock lock = new ReentrantLock();
* Returns list of all stream names
*/
public static Set<String> getStreamNames() {
- return listenersByStreamName.keySet();
+ return dataChangeListener.keySet();
}
/**
* @return {@link ListenerAdapter} specified by stream name.
*/
public static ListenerAdapter getListenerFor(final String streamName) {
- return listenersByStreamName.get(streamName);
+ return dataChangeListener.get(streamName);
}
/**
* @return True if the listener exist, false otherwise.
*/
public static boolean existListenerFor(final String streamName) {
- return listenersByStreamName.containsKey(streamName);
+ return dataChangeListener.containsKey(streamName);
}
/**
- * Creates new {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+ * Creates new {@link ListenerAdapter} listener from
+ * {@link YangInstanceIdentifier} path and stream name.
*
* @param path
* Path to data in data repository.
* @param streamName
* The name of the stream.
- * @return New {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+ * @param outputType
+ * - Spcific type of output for notifications - XML or JSON
+ * @return New {@link ListenerAdapter} listener from
+ * {@link YangInstanceIdentifier} path and stream name.
*/
- public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName) {
- final ListenerAdapter listener = new ListenerAdapter(path, streamName);
+ public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName,
+ final NotificationOutputType outputType) {
+ final ListenerAdapter listener = new ListenerAdapter(path, streamName, outputType);
try {
lock.lock();
- listenersByStreamName.put(streamName, listener);
+ dataChangeListener.put(streamName, listener);
} finally {
lock.unlock();
}
* Removes all listeners.
*/
public static void removeAllListeners() {
- for (final ListenerAdapter listener : listenersByStreamName.values()) {
+ for (final ListenerAdapter listener : dataChangeListener.values()) {
try {
listener.close();
} catch (final Exception e) {
}
try {
lock.lock();
- listenersByStreamName = new ConcurrentHashMap<>();
+ dataChangeListener = new ConcurrentHashMap<>();
} finally {
lock.unlock();
}
}
try {
lock.lock();
- listenersByStreamName.remove(listener.getStreamName());
+ dataChangeListener.remove(listener.getStreamName());
} finally {
lock.unlock();
}
}
+ /**
+ * Prepare listener for notification ({@link NotificationDefinition})
+ *
+ * @param paths
+ * - paths of notifications
+ * @param streamName
+ * - name of stream (generated by paths)
+ * @param outputType
+ * - type of output for onNotification - XML or JSON
+ * @return List of {@link NotificationListenerAdapter} by paths
+ */
public static List<NotificationListenerAdapter> createNotificationListener(final List<SchemaPath> paths,
final String streamName, final String outputType) {
final List<NotificationListenerAdapter> listListeners = new ArrayList<>();
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+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.NodeIdentifier;
public final class CreateStreamUtil {
private static final Logger LOG = LoggerFactory.getLogger(CreateStreamUtil.class);
+ private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
private CreateStreamUtil() {
throw new UnsupportedOperationException("Util class");
final ContainerNode output = ImmutableContainerNodeBuilder.create()
.withNodeIdentifier(new NodeIdentifier(outputQname))
.withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+ final NotificationOutputType outputType = prepareOutputType(data);
if (!Notificator.existListenerFor(streamName)) {
- Notificator.createListener(path, streamName);
+ Notificator.createListener(path, streamName, outputType);
}
return new DefaultDOMRpcResult(output);
}
+ /**
+ * @param data
+ * - data of notification
+ * @return output type fo notification
+ */
+ private static NotificationOutputType prepareOutputType(final ContainerNode data) {
+ NotificationOutputType outputType = parseEnum(data, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME);
+ return outputType = outputType == null ? NotificationOutputType.XML : outputType;
+ }
+
private static String prepareStream(final YangInstanceIdentifier path, final SchemaContext schemaContext,
final ContainerNode data) {
LogicalDatastoreType ds = parseEnum(data, LogicalDatastoreType.class,
}
SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision);
SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets
- .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+ .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+ QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
}
private RestconfStreamsConstants() {
revision "2014-07-08" {
}
+ grouping notification-output-type-grouping{
+ leaf notification-output-type {
+ type enumeration {
+ enum JSON;
+ enum XML;
+ }
+ default "XML";
+ description "Input parameter which type of output will be parsed on notification";
+ }
+ }
+
augment "/salrmt:create-data-change-event-subscription/salrmt:input" {
leaf datastore {
type enumeration {
enum SUBTREE;
}
}
+ uses notification-output-type-grouping;
}
augment "/salrmt:create-notification-stream/salrmt:input" {
- leaf notification-output-type {
- type enumeration {
- enum JSON;
- enum XML;
- }
- default "XML";
- description "Input parameter which type of output will be parsed on notification";
- }
+ uses notification-output-type-grouping;
}
}
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private final BrokerFacade brokerFacade = BrokerFacade.getInstance();
private final NormalizedNode<?, ?> dummyNode = createDummyNode("test:module", "2014-01-09", "interfaces");
private final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> dummyNodeInFuture =
- wrapDummyNode(dummyNode);
+ wrapDummyNode(this.dummyNode);
private final QName qname = TestUtils.buildQName("interfaces","test:module", "2014-01-09");
- private final SchemaPath type = SchemaPath.create(true, qname);
- private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(qname).build();
+ private final SchemaPath type = SchemaPath.create(true, this.qname);
+ private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(this.qname).build();
@Before
public void setUp() throws Exception {
@Test
public void testRegisterToListenDataChanges() {
- final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream");
+ final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream",
+ NotificationOutputType.XML);
@SuppressWarnings("unchecked")
final ListenerRegistration<DOMDataChangeListener> mockRegistration = mock(ListenerRegistration.class);
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil.getRevisionFormat;
+import com.google.common.base.Preconditions;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.Date;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+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.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
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.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
}
@Test
- @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
public void resolveURIParametersConcreteValues() {
resolveURIParameters("OPERATIONAL", "SUBTREE", LogicalDatastoreType.OPERATIONAL, DataChangeScope.SUBTREE);
}
@Test
- @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
public void resolveURIParametersDefaultValues() {
resolveURIParameters(null, null, LogicalDatastoreType.CONFIGURATION, DataChangeScope.BASE);
}
final String datastoreValue = datastore == null ? "CONFIGURATION" : datastore;
final String scopeValue = scope == null ? "BASE" : scope + "";
Notificator.createListener(iiBuilder.build(), "dummyStreamName/datastore=" + datastoreValue + "/scope="
- + scopeValue);
+ + scopeValue, NotificationOutputType.XML);
final UriInfo mockedUriInfo = mock(UriInfo.class);
@SuppressWarnings("unchecked")
final UriBuilder uriBuilder = UriBuilder.fromUri("www.whatever.com");
when(mockedUriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
-// when(mockedBrokerFacade.invokeRpc(any(SchemaPath.class), any(NormalizedNode.class)))
-// .thenReturn(Futures.<DOMRpcResult, DOMRpcException> immediateCheckedFuture(new DefaultDOMRpcResult(Builders.containerBuilder().build())));
-
this.restconf.invokeRpc("sal-remote:create-data-change-event-subscription", prepareDomRpcNode(datastore, scope),
mockedUriInfo);
- final ListenerAdapter listener = Notificator.getListenerFor("opendaylight-inventory:nodes/datastore="
+ final ListenerAdapter listener =
+ Notificator.getListenerFor("data-change-event-subscription/opendaylight-inventory:nodes/datastore="
+ datastoreValue + "/scope=" + scopeValue);
assertNotNull(listener);
-
}
private NormalizedNodeContext prepareDomRpcNode(final String datastore, final String scope) {
final Module rpcSalRemoteModule = schema.findModuleByName("sal-remote", revDate);
final Set<RpcDefinition> setRpcs = rpcSalRemoteModule.getRpcs();
final QName rpcQName = QName.create(rpcSalRemoteModule.getQNameModule(), "create-data-change-event-subscription");
- final QName rpcInputQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote","2014-01-14","input");
+ final QName rpcInputQName =
+ QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "input");
+ final RpcDefinition rpcDef = Mockito.mock(RpcDefinition.class);
ContainerSchemaNode rpcInputSchemaNode = null;
for (final RpcDefinition rpc : setRpcs) {
if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
}
assertNotNull("RPC ContainerSchemaNode was not found!", rpcInputSchemaNode);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container =
+ Builders.containerBuilder(rpcInputSchemaNode);
- final QName pathQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
+ final QName pathQName =
+ QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
final DataSchemaNode pathSchemaNode = rpcInputSchemaNode.getDataChildByName(pathQName);
assertTrue(pathSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> pathNode = (Builders.leafBuilder((LeafSchemaNode) pathSchemaNode)
- .withValue(YangInstanceIdentifier.builder().node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
+ .withValue(YangInstanceIdentifier.builder()
+ .node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
container.withChild(pathNode);
+ final AugmentationSchema augmentationSchema = rpcInputSchemaNode.getAvailableAugmentations().iterator().next();
+ Preconditions.checkNotNull(augmentationSchema);
+ final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder =
+ Builders.augmentationBuilder(augmentationSchema);
+
final QName dataStoreQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "datastore");
- final DataSchemaNode dsSchemaNode = rpcInputSchemaNode.getDataChildByName(dataStoreQName);
+ final DataSchemaNode dsSchemaNode = augmentationSchema.getDataChildByName(dataStoreQName);
assertTrue(dsSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> dsNode = (Builders.leafBuilder((LeafSchemaNode) dsSchemaNode)
.withValue(datastore)).build();
- container.withChild(dsNode);
+ augmentationBuilder.withChild(dsNode);
final QName scopeQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "scope");
- final DataSchemaNode scopeSchemaNode = rpcInputSchemaNode.getDataChildByName(scopeQName);
+ final DataSchemaNode scopeSchemaNode = augmentationSchema.getDataChildByName(scopeQName);
assertTrue(scopeSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> scopeNode = (Builders.leafBuilder((LeafSchemaNode) scopeSchemaNode)
.withValue(scope)).build();
- container.withChild(scopeNode);
+ augmentationBuilder.withChild(scopeNode);
+
+ final QName outputQName =
+ QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "notification-output-type");
+ final DataSchemaNode outputSchemaNode = augmentationSchema.getDataChildByName(outputQName);
+ assertTrue(outputSchemaNode instanceof LeafSchemaNode);
+ final LeafNode<Object> outputNode =
+ (Builders.leafBuilder((LeafSchemaNode) outputSchemaNode).withValue("XML")).build();
+ augmentationBuilder.withChild(outputNode);
+
+ container.withChild(augmentationBuilder.build());
+
+ when(rpcDef.getInput()).thenReturn(rpcInputSchemaNode);
+ when(rpcDef.getPath()).thenReturn(SchemaPath.create(true, rpcQName));
+ when(rpcDef.getQName()).thenReturn(rpcQName);
- return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+ return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null, rpcDef, null, schema),
+ container.build());
}
}
import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
Notificator.removeAllListeners();
// put test streams
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0));
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1));
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2));
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0),
+ NotificationOutputType.XML);
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1),
+ NotificationOutputType.XML);
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2),
+ NotificationOutputType.XML);
}
@AfterClass
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class RestconfStreamsSubscriptionServiceImplTest {
- private static final String uri = "/restconf/15/data/ietf-restconf-monitoring:restconf-state/streams/stream/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
+ private static final String uri = "/restconf/17/data/ietf-restconf-monitoring:restconf-state/streams/stream/"
+ + "toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
private static Field listenersByStreamName;
@Mock
MockitoAnnotations.initMocks(this);
final DOMDataBroker dataBroker = mock(DOMDataBroker.class);
final ListenerRegistration<DOMDataChangeListener> listener = mock(ListenerRegistration.class);
- doReturn(dataBroker).when(dataBrokerHandler).get();
+ doReturn(dataBroker).when(this.dataBrokerHandler).get();
doReturn(listener).when(dataBroker).registerDataChangeListener(any(), any(), any(), any());
}
final ListenerAdapter adapter = mock(ListenerAdapter.class);
doReturn(false).when(adapter).isListening();
listenersByStreamNameSetter.put("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", adapter);
- listenersByStreamName = Notificator.class.getDeclaredField("listenersByStreamName");
+ listenersByStreamName = Notificator.class.getDeclaredField("dataChangeListener");
listenersByStreamName.setAccessible(true);
listenersByStreamName.set(Notificator.class, listenersByStreamNameSetter);
}
@Test
- public void testSubscribeToStream() {
+ public void testSubscribueToStream() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", uriInfo);
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler);
+ final Response response = streamsSubscriptionService
+ .subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", this.uriInfo);
assertEquals(200, response.getStatus());
- assertEquals("ws://:8181/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", response.getHeaderString("Location"));
+ assertEquals("ws://:8181/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+ response.getHeaderString("Location"));
}
@Test(expected = RestconfDocumentedException.class)
public void testSubscribeToStreamMissingDatastoreInPath() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", uriInfo);
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler);
+ streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", this.uriInfo);
}
@Test(expected = RestconfDocumentedException.class)
public void testSubscribeToStreamMissingScopeInPath() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL", uriInfo);
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler);
+ streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL",
+ this.uriInfo);
}
}
enum SUBTREE;
}
}
+ leaf notification-output-type {
+ type enumeration {
+ enum JSON;
+ enum XML;
+ }
+ default "XML";
+ description "Input parameter which type of output will be parsed on notification";
+ }
}
}
\ No newline at end of file