X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Fconnect%2Fnetconf%2Fschema%2Fmapping%2FNetconfMessageTransformer.java;h=734abb7978b910f0d5c3bf0ca4a069926674026c;hb=72543f819bb65c6666c0ba1f49d19f2251c8f518;hp=668415aa6b1a3387b3d88a80e44fa634637e3ce0;hpb=4f25b2a0e46f8ba067ad032bc0db71fd18cebe60;p=netconf.git diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java index 668415aa6b..734abb7978 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java @@ -7,57 +7,39 @@ */ package org.opendaylight.netconf.sal.connect.netconf.schema.mapping; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EVENT_TIME; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; -import com.google.common.base.Function; import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.AbstractMap; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Date; -import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.dom.DOMResult; -import org.opendaylight.controller.config.util.xml.DocumentedException; import org.opendaylight.controller.config.util.xml.MissingNameSpaceException; import org.opendaylight.controller.config.util.xml.XmlElement; -import org.opendaylight.controller.config.util.xml.XmlUtil; import org.opendaylight.controller.md.sal.dom.api.DOMEvent; import org.opendaylight.controller.md.sal.dom.api.DOMNotification; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.api.NetconfMessage; -import org.opendaylight.netconf.notifications.NetconfNotification; import org.opendaylight.netconf.sal.connect.api.MessageTransformer; import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.MessageCounter; -import org.opendaylight.netconf.util.NetconfUtil; -import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; -import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.SchemaOrderedNormalizedNodeWriter; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; @@ -72,65 +54,7 @@ import org.w3c.dom.Element; public class NetconfMessageTransformer implements MessageTransformer { - public enum BaseSchema { - - BASE_NETCONF_CTX( - Lists.newArrayList( - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance(), - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.$YangModuleInfoImpl.getInstance() - ) - ), - BASE_NETCONF_CTX_WITH_NOTIFICATIONS( - Lists.newArrayList( - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.$YangModuleInfoImpl.getInstance(), - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.$YangModuleInfoImpl.getInstance(), - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance(), - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.$YangModuleInfoImpl.getInstance() - ) - ); - - private final Map mappedRpcs; - private final SchemaContext schemaContext; - - BaseSchema(List modules) { - try { - final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); - moduleInfoBackedContext.addModuleInfos(modules); - schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get(); - mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION); - } catch (final RuntimeException e) { - LOG.error("Unable to prepare schema context for base netconf ops", e); - throw new ExceptionInInitializerError(e); - } - } - - private Map getMappedRpcs() { - return mappedRpcs; - } - - public SchemaContext getSchemaContext() { - return schemaContext; - } - } - - public static final String MESSAGE_ID_PREFIX = "m"; - - private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformer.class); - - - private static final Function QNAME_FUNCTION = new Function() { - @Override - public QName apply(final SchemaNode rpcDefinition) { - return rpcDefinition.getQName(); - } - }; - - private static final Function QNAME_NOREV_FUNCTION = new Function() { - @Override - public QName apply(final SchemaNode notification) { - return QNAME_FUNCTION.apply(notification).withoutRevision(); - } - }; + private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageTransformer.class); private final SchemaContext schemaContext; private final BaseSchema baseSchema; @@ -143,35 +67,38 @@ public class NetconfMessageTransformer implements MessageTransformer node.getQName().withoutRevision()); this.baseSchema = baseSchema; } @Override public synchronized DOMNotification toNotification(final NetconfMessage message) { - final Map.Entry stripped = stripNotification(message); + final Map.Entry stripped = NetconfMessageTransformUtil.stripNotification(message); final QName notificationNoRev; try { - notificationNoRev = QName.create(stripped.getValue().getNamespace(), stripped.getValue().getName()).withoutRevision(); + notificationNoRev = QName.create( + stripped.getValue().getNamespace(), stripped.getValue().getName()).withoutRevision(); } catch (final MissingNameSpaceException e) { - throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot find namespace", e); + throw new IllegalArgumentException( + "Unable to parse notification " + message + ", cannot find namespace", e); } - final Collection notificationDefinitions = mappedNotifications.get(notificationNoRev); Preconditions.checkArgument(notificationDefinitions.size() > 0, - "Unable to parse notification %s, unknown notification. Available notifications: %s", notificationDefinitions, mappedNotifications.keySet()); + "Unable to parse notification %s, unknown notification. Available notifications: %s", + notificationDefinitions, mappedNotifications.keySet()); - // FIXME if multiple revisions for same notifications are present, we should pick the most recent. Or ? - // We should probably just put the most recent notification versions into our map. We can expect that the device sends the data according to the latest available revision of a model. - final NotificationDefinition next = notificationDefinitions.iterator().next(); + final NotificationDefinition mostRecentNotification = getMostRecentNotification(notificationDefinitions); - // We wrap the notification as a container node in order to reuse the parsers and builders for container node - final ContainerSchemaNode notificationAsContainerSchemaNode = NetconfMessageTransformUtil.createSchemaForNotification(next); + final ContainerSchemaNode notificationAsContainerSchemaNode = + NetconfMessageTransformUtil.createSchemaForNotification(mostRecentNotification); final Element element = stripped.getValue().getDomElement(); final ContainerNode content; @@ -184,61 +111,12 @@ public class NetconfMessageTransformer implements MessageTransformer EVENT_TIME_FORMAT = new ThreadLocal() { - @Override - protected SimpleDateFormat initialValue() { - - final SimpleDateFormat withMillis = new SimpleDateFormat( - NetconfNotification.RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT); - - return new SimpleDateFormat(NetconfNotification.RFC3339_DATE_FORMAT_BLUEPRINT) { - private static final long serialVersionUID = 1L; - - @Override public Date parse(final String source) throws ParseException { - try { - return super.parse(source); - } catch (ParseException e) { - // In case of failure, try to parse with milliseconds - return withMillis.parse(source); - } - } - }; - } - - @Override - public void set(final SimpleDateFormat value) { - throw new UnsupportedOperationException(); - } - }; - - // FIXME move somewhere to util - private static Map.Entry stripNotification(final NetconfMessage message) { - final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument()); - final List childElements = xmlElement.getChildElements(); - Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format", message); - - final XmlElement eventTimeElement; - final XmlElement notificationElement; - - if (childElements.get(0).getName().equals(EVENT_TIME)) { - eventTimeElement = childElements.get(0); - notificationElement = childElements.get(1); - } - else if(childElements.get(1).getName().equals(EVENT_TIME)) { - eventTimeElement = childElements.get(1); - notificationElement = childElements.get(0); - } else { - throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message); - } + private static NotificationDefinition getMostRecentNotification( + final Collection notificationDefinitions) { + Comparator cmp = (o1, o2) -> + o1.getQName().getRevision().compareTo(o2.getQName().getRevision()); - try { - return new AbstractMap.SimpleEntry<>(EVENT_TIME_FORMAT.get().parse(eventTimeElement.getTextContent()), notificationElement); - } catch (DocumentedException e) { - throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message); - } catch (ParseException e) { - LOG.warn("Unable to parse event time from {}. Setting time to {}", eventTimeElement, NetconfNotification.UNKNOWN_EVENT_TIME, e); - return new AbstractMap.SimpleEntry<>(NetconfNotification.UNKNOWN_EVENT_TIME, notificationElement); - } + return Collections.max(notificationDefinitions, cmp); } @Override @@ -247,16 +125,19 @@ public class NetconfMessageTransformer implements MessageTransformer currentMappedRpcs = mappedRpcs; - // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf + // Determine whether a base netconf operation is being invoked + // and also check if the device exposed model for base netconf. // If no, use pre built base netconf operations model final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseOrNotificationRpc(rpcQName); - if(needToUseBaseCtx) { + if (needToUseBaseCtx) { currentMappedRpcs = baseSchema.getMappedRpcs(); } - Preconditions.checkNotNull(currentMappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, currentMappedRpcs.keySet()); - if(currentMappedRpcs.get(rpcQName).getInput() == null) { - return new NetconfMessage(prepareDomResultForRpcRequest(rpcQName).getNode().getOwnerDocument()); + Preconditions.checkNotNull(currentMappedRpcs.get(rpcQName), + "Unknown rpc %s, available rpcs: %s", rpcQName, currentMappedRpcs.keySet()); + if (currentMappedRpcs.get(rpcQName).getInput().getChildNodes().isEmpty()) { + return new NetconfMessage(NetconfMessageTransformUtil + .prepareDomResultForRpcRequest(rpcQName, counter).getNode().getOwnerDocument()); } Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpcQName); @@ -265,13 +146,14 @@ public class NetconfMessageTransformer implements MessageTransformer> value = normalized.getValue(); - normalizedNodeWriter.write(value); - normalizedNodeWriter.flush(); - } - } - } finally { - try { - writer.close(); - } catch (final Exception e) { - LOG.warn("Unable to close resource properly", e); - } - } - } @Override public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) { @@ -326,33 +176,39 @@ public class NetconfMessageTransformer implements MessageTransformer currentMappedRpcs = mappedRpcs; - // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf + // Determine whether a base netconf operation is being invoked + // and also check if the device exposed model for base netconf. // If no, use pre built base netconf operations model final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseOrNotificationRpc(rpcQName); - if(needToUseBaseCtx) { + if (needToUseBaseCtx) { currentMappedRpcs = baseSchema.getMappedRpcs(); } final RpcDefinition rpcDefinition = currentMappedRpcs.get(rpcQName); - Preconditions.checkArgument(rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpcQName); + Preconditions.checkArgument(rpcDefinition != null, + "Unable to parse response of %s, the rpc is unknown", rpcQName); // In case no input for rpc is defined, we can simply construct the payload here - if (rpcDefinition.getOutput() == null) { + if (rpcDefinition.getOutput().getChildNodes().isEmpty()) { Preconditions.checkArgument(XmlElement.fromDomDocument( message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(), "Unexpected content in response of rpc: %s, %s", rpcDefinition.getQName(), message); @@ -370,7 +226,7 @@ public class NetconfMessageTransformer implements MessageTransformer