<module>sal-rest-connector</module>
<module>sal-netconf-connector</module>
-
+
<module>inventory-manager</module>
<module>statistics-manager</module>
<module>topology-manager</module>
<module>sal-remoterpc-connector/implementation</module>
<!--module>clustered-data-store/implementation</module>
-->
-
+
</modules>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-impl</artifactId>
- <version>${yangtools.version}</version>
+ <version>${yangtools.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-parser-impl</artifactId>
- <version>${yangtools.version}</version>
+ <version>${yangtools.version}</version>
</dependency>
-
<!-- Testing Dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
<version>4.0</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena-rta</artifactId>
+ </dependency>
</dependencies>
</project>
// + "/src/test/resources/logback.xml"),
mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
- mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena-rta").versionAsInProject(),
+ //
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
systemPackages("sun.nio.ch"),
<artifactId>sample-toaster-consumer</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
+
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-provider</artifactId>
<version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena-rta</artifactId>
+ </dependency>
</dependencies>
</project>
*/
package org.opendaylight.controller.sample.toaster.it;
-import static org.junit.Assert.assertEquals;
-import static org.opendaylight.controller.test.sal.binding.it.TestHelper.*;
-import static org.ops4j.pax.exam.CoreOptions.*;
-
-import javax.inject.Inject;
-import javax.management.JMX;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
-import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.HashBrown;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WhiteBread;
import org.ops4j.pax.exam.util.Filter;
import org.ops4j.pax.exam.util.PathUtils;
+import javax.inject.Inject;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.baseModelBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.bindingAwareSalBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.configMinumumBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.flowCapableModelBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAndMockitoBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
@RunWith(PaxExam.class)
public class ToasterTest {
return new DefaultCompositeOption(
mavenBundle("org.opendaylight.controller.samples", "sample-toaster-provider").versionAsInProject(),
mavenBundle("org.opendaylight.controller.samples", "sample-toaster-consumer").versionAsInProject(),
- mavenBundle("org.opendaylight.controller.samples", "sample-toaster").versionAsInProject()
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena-rta").versionAsInProject()
);
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.opendaylight.controller.config.api.LookupRegistry;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.yangtools.yang.model.api.Module;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
/**
* Manages life cycle of {@link YangStoreSnapshot}.
public Optional<List<String>> getLocation() {
return Optional.absent();
}
+
+ @Override
+ public String toString() {
+ return capability;
+ }
}
private static class YangStoreCapability extends BasicCapability {
import org.opendaylight.controller.config.yang.test.impl.Peers;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+++ /dev/null
-/*
- * Copyright (c) 2013 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,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.api;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-
-import java.io.IOException;
-
-import org.opendaylight.protocol.framework.AbstractProtocolSession;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractNetconfSession<S extends NetconfSession, L extends NetconfSessionListener<S>> extends AbstractProtocolSession<NetconfMessage> implements NetconfSession {
- private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSession.class);
- private final L sessionListener;
- private final long sessionId;
- private boolean up = false;
-
- private final Channel channel;
-
- protected AbstractNetconfSession(L sessionListener, Channel channel, long sessionId) {
- this.sessionListener = sessionListener;
- this.channel = channel;
- this.sessionId = sessionId;
- logger.debug("Session {} created", sessionId);
- }
-
- protected abstract S thisInstance();
-
- @Override
- public void close() {
- channel.close();
- up = false;
- sessionListener.onSessionTerminated(thisInstance(), new NetconfTerminationReason("Session closed"));
- }
-
- @Override
- protected void handleMessage(NetconfMessage netconfMessage) {
- logger.debug("handling incoming message");
- sessionListener.onMessage(thisInstance(), netconfMessage);
- }
-
- @Override
- public ChannelFuture sendMessage(NetconfMessage netconfMessage) {
- return channel.writeAndFlush(netconfMessage);
- }
-
- @Override
- protected void endOfInput() {
- logger.debug("Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
- : "initialized");
- if (isUp()) {
- this.sessionListener.onSessionDown(thisInstance(), new IOException("End of input detected. Close the session."));
- }
- }
-
- @Override
- protected void sessionUp() {
- logger.debug("Session {} up", toString());
- sessionListener.onSessionUp(thisInstance());
- this.up = true;
- }
-
- @Override
- public String toString() {
- final StringBuffer sb = new StringBuffer("ServerNetconfSession{");
- sb.append("sessionId=").append(sessionId);
- sb.append('}');
- return sb.toString();
- }
-
- public final boolean isUp() {
- return up;
- }
-
- public final long getSessionId() {
- return sessionId;
- }
-}
-
--- /dev/null
+/*
+ * Copyright (c) 2013 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+/**
+ * The only input for the start of a NETCONF session is hello-message.
+ */
+public final class NetconfClientSessionPreferences extends NetconfSessionPreferences {
+
+ private final NetconfMessage startExiMessage;
+
+ public NetconfClientSessionPreferences(final NetconfMessage helloMessage,
+ final NetconfMessage startExiMessage) {
+ super(helloMessage);
+ this.startExiMessage = startExiMessage;
+ }
+
+ /**
+ * @return the startExiMessage
+ */
+ public NetconfMessage getStartExiMessage() {
+ return startExiMessage;
+ }
+}
private final long sessionId;
- public NetconfServerSessionPreferences(final NetconfMessage helloMessage, long sessionId) {
+ public NetconfServerSessionPreferences(final NetconfMessage helloMessage,
+ long sessionId) {
super(helloMessage);
this.sessionId = sessionId;
}
import org.opendaylight.protocol.framework.ProtocolSession;
public interface NetconfSession extends ProtocolSession<NetconfMessage> {
+
ChannelFuture sendMessage(NetconfMessage message);
+
}
package org.opendaylight.controller.netconf.api;
public class NetconfSessionPreferences {
+
private final NetconfMessage helloMessage;
public NetconfSessionPreferences(final NetconfMessage helloMessage) {
public NetconfMessage getHelloMessage() {
return this.helloMessage;
}
+
}
org.opendaylight.controller.netconf.util,
org.opendaylight.controller.netconf.util.*,
org.opendaylight.protocol.framework,
+ org.openexi.*,
org.slf4j,
org.w3c.dom,
org.xml.sax,
package org.opendaylight.controller.netconf.client;
+import com.google.common.base.Optional;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
-
-import java.io.Closeable;
-import java.net.InetSocketAddress;
-
import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.protocol.framework.AbstractDispatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
+import java.io.Closeable;
+import java.net.InetSocketAddress;
public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> implements Closeable {
package org.opendaylight.controller.netconf.client;
import io.netty.channel.Channel;
-
-import java.util.Collection;
-
-import org.opendaylight.controller.netconf.api.AbstractNetconfSession;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSession;
+import org.opendaylight.controller.netconf.util.handler.NetconfEXICodec;
+import org.opendaylight.controller.netconf.util.handler.NetconfEXIToMessageDecoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageToEXIEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageToXMLEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collection;
+
public final class NetconfClientSession extends AbstractNetconfSession<NetconfClientSession, NetconfClientSessionListener> {
private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
public NetconfClientSession(NetconfClientSessionListener sessionListener, Channel channel, long sessionId,
Collection<String> capabilities) {
- super(sessionListener,channel,sessionId);
+ super(sessionListener, channel, sessionId);
this.capabilities = capabilities;
logger.debug("Client Session {} created", toString());
}
return capabilities;
}
+
@Override
protected NetconfClientSession thisInstance() {
return this;
}
+
+ @Override
+ protected void addExiHandlers(NetconfEXICodec exiCodec) {
+ // TODO used only in negotiator, client supports only auto start-exi
+ replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec));
+ replaceMessageEncoder(new NetconfMessageToEXIEncoder(exiCodec));
+ }
+
+ @Override
+ public void stopExiCommunication() {
+ // TODO never used, Netconf client does not support stop-exi
+ replaceMessageDecoder(new NetconfXMLToMessageDecoder());
+ replaceMessageEncoder(new NetconfMessageToXMLEncoder());
+ }
}
package org.opendaylight.controller.netconf.client;
-import java.util.Collection;
-import java.util.List;
-
-import javax.annotation.Nullable;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
-import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
+import org.w3c.dom.NodeList;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.Timer;
import io.netty.util.concurrent.Promise;
public class NetconfClientSessionNegotiator extends
- AbstractNetconfSessionNegotiator<NetconfSessionPreferences, NetconfClientSession, NetconfClientSessionListener> {
+ AbstractNetconfSessionNegotiator<NetconfClientSessionPreferences, NetconfClientSession, NetconfClientSessionListener>
+{
+ private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionNegotiator.class);
+
+ private static final XPathExpression sessionIdXPath = XMLNetconfUtil
+ .compileXPath("/netconf:hello/netconf:session-id");
+
+ private static final String EXI_1_0_CAPABILITY_MARKER = "exi:1.0";
- protected NetconfClientSessionNegotiator(NetconfSessionPreferences sessionPreferences,
- Promise<NetconfClientSession> promise, Channel channel, Timer timer, NetconfClientSessionListener sessionListener,
- long connectionTimeoutMillis) {
+ protected NetconfClientSessionNegotiator(NetconfClientSessionPreferences sessionPreferences,
+ Promise<NetconfClientSession> promise,
+ Channel channel,
+ Timer timer,
+ NetconfClientSessionListener sessionListener,
+ long connectionTimeoutMillis) {
super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis);
}
- private static Collection<String> getCapabilities(Document doc) {
- XmlElement responseElement = XmlElement.fromDomDocument(doc);
- XmlElement capabilitiesElement = responseElement
- .getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CAPABILITIES);
- List<XmlElement> caps = capabilitiesElement.getChildElements(XmlNetconfConstants.CAPABILITY);
- return Collections2.transform(caps, new Function<XmlElement, String>() {
+ @Override
+ protected void handleMessage(NetconfHelloMessage netconfMessage) {
+ NetconfClientSession session = super.getSessionForHelloMessage(netconfMessage);
+
+ if (shouldUseExi(netconfMessage.getDocument())){
+ logger.info("Netconf session: {} should use exi.", session);
+ tryToStartExi(session);
+ } else {
+ logger.info("Netconf session {} isn't capable using exi.", session);
+ negotiationSuccessful(session);
+ }
+ }
+
+ private boolean shouldUseExi(Document doc) {
+ return containsExi10Capability(doc)
+ && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument());
+ }
+
+ private boolean containsExi10Capability(final Document doc) {
+ final NodeList nList = doc.getElementsByTagName(XmlNetconfConstants.CAPABILITY);
+ for (int i = 0; i < nList.getLength(); i++) {
+ if (nList.item(i).getTextContent().contains(EXI_1_0_CAPABILITY_MARKER)) {
+ return true;
+ }
+ }
+ return false;
+ }
- @Nullable
+ private void tryToStartExi(final NetconfClientSession session) {
+ final NetconfMessage startExi = sessionPreferences.getStartExiMessage();
+ session.sendMessage(startExi).addListener(new ChannelFutureListener() {
@Override
- public String apply(@Nullable XmlElement input) {
- // Trim possible leading/tailing whitespace
- return input.getTextContent().trim();
+ public void operationComplete(final ChannelFuture f) {
+ if (!f.isSuccess()) {
+ logger.warn("Failed to send start-exi message {} on session {}", startExi, session, f.cause());
+ } else {
+ logger.trace("Start-exi message {} sent to socket on session {}", startExi, session);
+ NetconfClientSessionNegotiator.this.channel.pipeline().addAfter(
+ AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
+ new ExiConfirmationInboundHandler(session));
+ }
}
});
}
- private static final XPathExpression sessionIdXPath = XMLNetconfUtil
- .compileXPath("/netconf:hello/netconf:session-id");
-
private long extractSessionId(Document doc) {
final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
String textContent = sessionIdNode.getTextContent();
@Override
protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfHelloMessage message) {
return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
- getCapabilities(message.getDocument()));
+ NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()));
+ }
+
+ /**
+ * Handler to process response for start-exi message
+ */
+ private final class ExiConfirmationInboundHandler extends ChannelInboundHandlerAdapter {
+ private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
+
+ private final NetconfClientSession session;
+
+ ExiConfirmationInboundHandler(NetconfClientSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER);
+
+ NetconfMessage netconfMessage = (NetconfMessage) msg;
+
+ // Ok response to start-exi, try to add exi handlers
+ if (NetconfMessageUtil.isOKMessage(netconfMessage)) {
+ logger.trace("Positive response on start-exi call received on session {}", session);
+ try {
+ session.startExiCommunication(sessionPreferences.getStartExiMessage());
+ } catch (RuntimeException e) {
+ // Unable to add exi, continue without exi
+ logger.warn("Unable to start exi communication, Communication will continue without exi on session {}", session, e);
+ }
+
+ // Error response
+ } else if(NetconfMessageUtil.isErrorMessage(netconfMessage)) {
+ logger.warn(
+ "Error response to start-exi message {}, Communication will continue without exi on session {}",
+ XmlUtil.toString(netconfMessage.getDocument()), session);
+
+ // Unexpected response to start-exi, throwing message away, continue without exi
+ } else {
+ logger.warn(
+ "Unexpected response to start-exi message, should be ok, was {}, " +
+ "Communication will continue without exi and response message will be thrown away on session {}",
+ XmlUtil.toString(netconfMessage.getDocument()), session);
+ }
+
+ negotiationSuccessful(session);
+ }
}
}
package org.opendaylight.controller.netconf.client;
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
-
-import java.io.IOException;
-import java.io.InputStream;
-
+import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.netconf.util.messages.NetconfStartExiMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.protocol.framework.SessionListenerFactory;
import org.opendaylight.protocol.framework.SessionNegotiator;
import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
-import org.xml.sax.SAXException;
+import org.openexi.proc.common.AlignmentType;
+import org.openexi.proc.common.EXIOptions;
+import org.openexi.proc.common.EXIOptionsException;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfMessage, NetconfClientSession, NetconfClientSessionListener> {
+ public static final java.util.Set<String> CLIENT_CAPABILITIES = Sets.newHashSet(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_1,
+ XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0);
+
+ private static final String START_EXI_MESSAGE_ID = "default-start-exi";
+
private final Optional<NetconfHelloMessageAdditionalHeader> additionalHeader;
private final long connectionTimeoutMillis;
private final Timer timer;
+ private final EXIOptions options;
+
+ public NetconfClientSessionNegotiatorFactory(Timer timer,
+ Optional<NetconfHelloMessageAdditionalHeader> additionalHeader,
+ long connectionTimeoutMillis) {
+ this(timer, additionalHeader, connectionTimeoutMillis, DEFAULT_OPTIONS);
+ }
- public NetconfClientSessionNegotiatorFactory(Timer timer, Optional<NetconfHelloMessageAdditionalHeader> additionalHeader, long connectionTimeoutMillis) {
+ public NetconfClientSessionNegotiatorFactory(Timer timer,
+ Optional<NetconfHelloMessageAdditionalHeader> additionalHeader,
+ long connectionTimeoutMillis, EXIOptions exiOptions) {
this.timer = Preconditions.checkNotNull(timer);
this.additionalHeader = additionalHeader;
this.connectionTimeoutMillis = connectionTimeoutMillis;
- }
-
- private static NetconfMessage loadHelloMessageTemplate() {
- final String helloMessagePath = "/client_hello.xml";
- try (InputStream is = NetconfClientSessionNegotiatorFactory.class.getResourceAsStream(helloMessagePath)) {
- Preconditions.checkState(is != null, "Input stream from %s was null", helloMessagePath);
- return new NetconfMessage(XmlUtil.readXmlToDocument(is));
- } catch (SAXException | IOException e) {
- throw new RuntimeException("Unable to load hello message", e);
- }
+ this.options = exiOptions;
}
@Override
- public SessionNegotiator<NetconfClientSession> getSessionNegotiator(SessionListenerFactory<NetconfClientSessionListener> sessionListenerFactory, Channel channel,
+ public SessionNegotiator<NetconfClientSession> getSessionNegotiator(SessionListenerFactory<NetconfClientSessionListener> sessionListenerFactory,
+ Channel channel,
Promise<NetconfClientSession> promise) {
- // Hello message needs to be recreated every time
- NetconfMessage helloMessage = loadHelloMessageTemplate();
- if(this.additionalHeader.isPresent()) {
- helloMessage = new NetconfHelloMessage(helloMessage.getDocument(), additionalHeader.get());
- } else {
- helloMessage = new NetconfHelloMessage(helloMessage.getDocument());
- }
+ NetconfMessage startExiMessage = NetconfStartExiMessage.create(options, START_EXI_MESSAGE_ID);
+ NetconfHelloMessage helloMessage = NetconfHelloMessage.createClientHello(CLIENT_CAPABILITIES, additionalHeader);
- NetconfSessionPreferences proposal = new NetconfSessionPreferences(helloMessage);
+ NetconfClientSessionPreferences proposal = new NetconfClientSessionPreferences(helloMessage,startExiMessage);
return new NetconfClientSessionNegotiator(proposal, promise, channel, timer,
- sessionListenerFactory.getSessionListener(), connectionTimeoutMillis);
+ sessionListenerFactory.getSessionListener(),connectionTimeoutMillis);
+ }
+
+ private static final EXIOptions DEFAULT_OPTIONS = new EXIOptions();
+ static {
+ try {
+ DEFAULT_OPTIONS.setPreserveDTD(true);
+ DEFAULT_OPTIONS.setPreserveNS(true);
+ DEFAULT_OPTIONS.setPreserveLexicalValues(true);
+ DEFAULT_OPTIONS.setAlignmentType(AlignmentType.preCompress);
+ } catch (EXIOptionsException e) {
+ // Should not happen since DEFAULT_OPTIONS are still the same
+ throw new IllegalStateException("Unable to create EXI DEFAULT_OPTIONS");
+ }
}
}
+++ /dev/null
-<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <capabilities>
- <capability>urn:ietf:params:netconf:base:1.0</capability>
- <capability>urn:ietf:params:netconf:base:1.1</capability>
- </capabilities>
-</hello>
org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210,
org.opendaylight.yangtools.yang.binding,
+ org.openexi.*,
</Import-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.impl;
-import io.netty.channel.Channel;
-
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.opendaylight.controller.netconf.api.AbstractNetconfSession;
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSession;
+import org.opendaylight.controller.netconf.util.handler.NetconfEXICodec;
+import org.opendaylight.controller.netconf.util.handler.NetconfEXIToMessageDecoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageToEXIEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageToXMLEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
public final class NetconfServerSession extends AbstractNetconfSession<NetconfServerSession, NetconfServerSessionListener> implements NetconfManagementSession {
protected NetconfServerSession thisInstance() {
return this;
}
+
+ @Override
+ protected void addExiHandlers(NetconfEXICodec exiCodec) {
+ replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec));
+ replaceMessageEncoderAfterNextMessage(new NetconfMessageToEXIEncoder(exiCodec));
+ }
+
+ @Override
+ public void stopExiCommunication() {
+ replaceMessageDecoder(new NetconfXMLToMessageDecoder());
+ replaceMessageEncoderAfterNextMessage(new NetconfMessageToXMLEncoder());
+ }
}
package org.opendaylight.controller.netconf.impl;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.api.NetconfSessionListener;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
-import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
public class NetconfServerSessionListener implements NetconfSessionListener<NetconfServerSession> {
- public static final String MESSAGE_ID = "message-id";
static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
private final SessionMonitoringService monitoringService;
logger.info("Session {} closed successfully", session.getSessionId());
}
- private NetconfMessage processDocument(final NetconfMessage netconfMessage,
- NetconfServerSession session) throws NetconfDocumentedException {
+ private NetconfMessage processDocument(final NetconfMessage netconfMessage, NetconfServerSession session)
+ throws NetconfDocumentedException {
- final Document incommingDocument = netconfMessage.getDocument();
- final Node rootNode = incommingDocument.getDocumentElement();
+ final Document incomingDocument = netconfMessage.getDocument();
+ final Node rootNode = incomingDocument.getDocumentElement();
if (rootNode.getLocalName().equals(XmlNetconfConstants.RPC_KEY)) {
- final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent();
- checkState(messageId != null);
final Document responseDocument = XmlUtil.newDocument();
- Document rpcReply = operationRouter.onNetconfMessage(
- incommingDocument, session);
+ checkMessageId(rootNode);
+
+ Document rpcReply = operationRouter.onNetconfMessage(incomingDocument, session);
session.onIncommingRpcSuccess();
}
}
- private static boolean isCloseSession(final NetconfMessage incommingDocument) {
- final Document document = incommingDocument.getDocument();
+ private void checkMessageId(Node rootNode) throws NetconfDocumentedException {
+ NamedNodeMap attributes = rootNode.getAttributes();
+ if(attributes.getNamedItemNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlNetconfConstants.MESSAGE_ID)!=null) {
+ return;
+ }
+
+ if(attributes.getNamedItem(XmlNetconfConstants.MESSAGE_ID)!=null) {
+ return;
+ }
+
+ throw new NetconfDocumentedException("Missing attribute" + rootNode.getNodeName(),
+ NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.missing_attribute,
+ NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of(NetconfDocumentedException.ErrorTag.missing_attribute.toString(),
+ XmlNetconfConstants.MESSAGE_ID));
+ }
+
+ private static boolean isCloseSession(final NetconfMessage incomingDocument) {
+ final Document document = incomingDocument.getDocument();
XmlElement rpcElement = XmlElement.fromDomDocument(document);
- if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent())
+ if (rpcElement.getOnlyChildElementOptionally(DefaultCloseSession.CLOSE_SESSION).isPresent()) {
return true;
+ }
return false;
}
package org.opendaylight.controller.netconf.impl;
-import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
package org.opendaylight.controller.netconf.impl;
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
-
-import java.io.InputStream;
+import static org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider.NetconfOperationProviderUtil.getNetconfSessionIdForReporting;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
+import java.util.Set;
import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
-import org.opendaylight.controller.netconf.util.NetconfUtil;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
-import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.protocol.framework.SessionListenerFactory;
import org.opendaylight.protocol.framework.SessionNegotiator;
import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
-import static org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider.NetconfOperationProviderUtil.getNetconfSessionIdForReporting;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfHelloMessage, NetconfServerSession, NetconfServerSessionListener> {
- public static final String SERVER_HELLO_XML_LOCATION = "/server_hello.xml";
+ private static final Set<String> DEFAULT_CAPABILITIES = Sets.newHashSet(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0);
private final Timer timer;
- private static final Document helloMessageTemplate = loadHelloMessageTemplate();
private final SessionIdProvider idProvider;
private final NetconfOperationProvider netconfOperationProvider;
private final long connectionTimeoutMillis;
this.monitoringService = monitoringService;
}
- private static Document loadHelloMessageTemplate() {
- InputStream resourceAsStream = NetconfServerSessionNegotiatorFactory.class
- .getResourceAsStream(SERVER_HELLO_XML_LOCATION);
- Preconditions.checkNotNull(resourceAsStream, "Unable to load server hello message blueprint from %s",
- SERVER_HELLO_XML_LOCATION);
- return NetconfUtil.createMessage(resourceAsStream).getDocument();
- }
-
/**
*
* @param defunctSessionListenerFactory will not be taken into account as session listener factory can
sessionListenerFactory.getSessionListener(), connectionTimeoutMillis);
}
- private static final XPathExpression sessionIdXPath = XMLNetconfUtil
- .compileXPath("/netconf:hello/netconf:session-id");
- private static final XPathExpression capabilitiesXPath = XMLNetconfUtil
- .compileXPath("/netconf:hello/netconf:capabilities");
-
private NetconfHelloMessage createHelloMessage(long sessionId, CapabilityProvider capabilityProvider) {
- Document helloMessageTemplate = getHelloTemplateClone();
-
- // change session ID
- final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, helloMessageTemplate,
- XPathConstants.NODE);
- sessionIdNode.setTextContent(String.valueOf(sessionId));
-
- // add capabilities from yang store
- final Element capabilitiesElement = (Element) XmlUtil.evaluateXPath(capabilitiesXPath, helloMessageTemplate,
- XPathConstants.NODE);
-
- for (String capability : capabilityProvider.getCapabilities()) {
- final Element capabilityElement = XmlUtil.createElement(helloMessageTemplate, XmlNetconfConstants.CAPABILITY, Optional.<String>absent());
- capabilityElement.setTextContent(capability);
- capabilitiesElement.appendChild(capabilityElement);
- }
- return new NetconfHelloMessage(helloMessageTemplate);
+ return NetconfHelloMessage.createServerHello(Sets.union(capabilityProvider.getCapabilities(), DEFAULT_CAPABILITIES), sessionId);
}
- private synchronized Document getHelloTemplateClone() {
- return (Document) helloMessageTemplate.cloneNode(true);
- }
}
import java.util.Map;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
-/*
- * Copyright (c) 2013 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,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.mapping.api;
-
-import org.opendaylight.controller.netconf.api.NetconfSession;
-
-public interface DefaultNetconfOperation {
- void setNetconfSession(NetconfSession s);
-}
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.netconf.impl.mapping.operations;\r
+\r
+import org.opendaylight.controller.netconf.impl.NetconfServerSession;\r
+\r
+public interface DefaultNetconfOperation {\r
+ void setNetconfSession(NetconfServerSession s);\r
+}\r
-/*
- * Copyright (c) 2013 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,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.impl.mapping.operations;
-
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfSession;
-import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
-import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import com.google.common.base.Optional;
-
-public class DefaultStartExi extends AbstractSingletonNetconfOperation implements DefaultNetconfOperation {
-
- public static final String START_EXI = "start-exi";
-
- private NetconfSession netconfSession;
-
- private static final Logger logger = LoggerFactory.getLogger(DefaultStartExi.class);
-
- public DefaultStartExi(String netconfSessionIdForReporting) {
- super(netconfSessionIdForReporting);
- }
-
- @Override
- protected String getOperationName() {
- return START_EXI;
- }
-
- @Override
- protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException {
-
- Element getSchemaResult = XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.of(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0));
-
- throw new UnsupportedOperationException("Not implemented");
-
- /*
- try {
- ExiParameters exiParams = new ExiParameters();
- exiParams.setParametersFromXmlElement(operationElement);
-
- netconfSession.addExiDecoder(ExiDecoderHandler.HANDLER_NAME, new ExiDecoderHandler(exiParams));
- netconfSession.addExiEncoderAfterMessageSent(ExiEncoderHandler.HANDLER_NAME,new ExiEncoderHandler(exiParams));
-
- } catch (EXIException e) {
- getSchemaResult = document
- .createElement(XmlNetconfConstants.RPC_ERROR);
- }
-
- logger.trace("{} operation successful", START_EXI);
- logger.debug("received start-exi message {} ", XmlUtil.toString(document));
- return getSchemaResult;
- */
- }
-
- @Override
- public void setNetconfSession(NetconfSession s) {
- netconfSession = s;
- }
-
- public NetconfSession getNetconfSession() {
- return netconfSession;
- }
-
-
-}
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.netconf.impl.mapping.operations;\r
+\r
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;\r
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;\r
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;\r
+import org.opendaylight.controller.netconf.api.NetconfMessage;\r
+import org.opendaylight.controller.netconf.impl.NetconfServerSession;\r
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;\r
+import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;\r
+import org.opendaylight.controller.netconf.util.xml.XmlElement;\r
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;\r
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+public class DefaultStartExi extends AbstractSingletonNetconfOperation implements DefaultNetconfOperation {\r
+ public static final String START_EXI = "start-exi";\r
+\r
+ private static final Logger logger = LoggerFactory.getLogger(DefaultStartExi.class);\r
+ private NetconfServerSession netconfSession;\r
+\r
+ public DefaultStartExi(String netconfSessionIdForReporting) {\r
+ super(netconfSessionIdForReporting);\r
+ }\r
+\r
+ @Override\r
+ public Document handle(Document message,\r
+ NetconfOperationChainedExecution subsequentOperation) throws NetconfDocumentedException {\r
+ logger.debug("Received start-exi message {} ", XmlUtil.toString(message));\r
+\r
+ try {\r
+ netconfSession.startExiCommunication(new NetconfMessage(message));\r
+ } catch (IllegalArgumentException e) {\r
+ throw new NetconfDocumentedException("Failed to parse EXI parameters", ErrorType.protocol,\r
+ ErrorTag.operation_failed, ErrorSeverity.error);\r
+ }\r
+\r
+ return super.handle(message, subsequentOperation);\r
+ }\r
+\r
+ @Override\r
+ protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException {\r
+ Element getSchemaResult = document.createElementNS( XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlNetconfConstants.OK);\r
+ logger.trace("{} operation successful", START_EXI);\r
+ return getSchemaResult;\r
+ }\r
+\r
+ @Override\r
+ protected String getOperationName() {\r
+ return START_EXI;\r
+ }\r
+\r
+ @Override\r
+ protected String getOperationNamespace() {\r
+ return XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0;\r
+ }\r
+\r
+ @Override\r
+ public void setNetconfSession(NetconfServerSession s) {\r
+ netconfSession = s;\r
+ }\r
+}\r
-/*
- * Copyright (c) 2013 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,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.impl.mapping.operations;
-
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfSession;
-import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
-import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-public class DefaultStopExi extends AbstractSingletonNetconfOperation implements DefaultNetconfOperation {
-
- public static final String STOP_EXI = "stop-exi";
- private NetconfSession netconfSession;
-
- private static final Logger logger = LoggerFactory
- .getLogger(DefaultStartExi.class);
-
- public DefaultStopExi(String netconfSessionIdForReporting) {
- super(netconfSessionIdForReporting);
- }
-
- @Override
- protected String getOperationName() {
- return STOP_EXI;
- }
-
- @Override
- protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement)
- throws NetconfDocumentedException {
- throw new UnsupportedOperationException("Not implemented");
- /*
- netconfSession.remove(ExiDecoderHandler.class);
- netconfSession.removeAfterMessageSent(ExiEncoderHandler.HANDLER_NAME);
-
- Element getSchemaResult = document.createElement(XmlNetconfConstants.OK);
- XmlUtil.addNamespaceAttr(getSchemaResult,
- XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
- logger.trace("{} operation successful", STOP_EXI);
- logger.debug("received stop-exi message {} ", XmlUtil.toString(document));
- return getSchemaResult;
- */
- }
-
- @Override
- public void setNetconfSession(NetconfSession s) {
- this.netconfSession = s;
- }
-
- public NetconfSession getNetconfSession() {
- return netconfSession;
- }
-}
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.netconf.impl.mapping.operations;\r
+\r
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
+import org.opendaylight.controller.netconf.impl.NetconfServerSession;\r
+import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;\r
+import org.opendaylight.controller.netconf.util.xml.XmlElement;\r
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;\r
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+\r
+public class DefaultStopExi extends AbstractSingletonNetconfOperation implements DefaultNetconfOperation {\r
+\r
+ public static final String STOP_EXI = "stop-exi";\r
+ private NetconfServerSession netconfSession;\r
+\r
+ private static final Logger logger = LoggerFactory\r
+ .getLogger(DefaultStartExi.class);\r
+\r
+ public DefaultStopExi(String netconfSessionIdForReporting) {\r
+ super(netconfSessionIdForReporting);\r
+ }\r
+\r
+ @Override\r
+ protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException {\r
+ logger.debug("Received stop-exi message {} ", XmlUtil.toString(operationElement));\r
+\r
+ netconfSession.stopExiCommunication();\r
+\r
+ Element getSchemaResult = document.createElementNS( XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlNetconfConstants.OK);\r
+ logger.trace("{} operation successful", STOP_EXI);\r
+ return getSchemaResult;\r
+ }\r
+\r
+ @Override\r
+ protected String getOperationName() {\r
+ return STOP_EXI;\r
+ }\r
+\r
+ @Override\r
+ protected String getOperationNamespace() {\r
+ return XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0;\r
+ }\r
+\r
+ @Override\r
+ public void setNetconfSession(NetconfServerSession s) {\r
+ this.netconfSession = s;\r
+ }\r
+}\r
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.api;
+package org.opendaylight.controller.netconf.impl.osgi;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.impl.NetconfServerSession;
import org.w3c.dom.Document;
public interface NetconfOperationRouter extends AutoCloseable {
- Document onNetconfMessage(Document message, NetconfSession session)
+ Document onNetconfMessage(Document message, NetconfServerSession session)
throws NetconfDocumentedException;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.NetconfServerSession;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStartExi;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStopExi;
-import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
@Override
public synchronized Document onNetconfMessage(Document message,
- NetconfSession session) throws NetconfDocumentedException {
+ NetconfServerSession session) throws NetconfDocumentedException {
Preconditions.checkNotNull(allNetconfOperations, "Operation router was not initialized properly");
NetconfOperationExecution netconfOperationExecution;
}
private NetconfOperationExecution getNetconfOperationWithHighestPriority(
- Document message, NetconfSession session) {
+ Document message, NetconfServerSession session) {
TreeMap<HandlingPriority, NetconfOperation> sortedByPriority = getSortedNetconfOperationsWithCanHandle(
message, session);
Preconditions.checkArgument(sortedByPriority.isEmpty() == false,
- "No %s available to handleWithNoSubsequentOperations message %s", NetconfOperation.class.getName(),
+ "No %s available to handle message %s", NetconfOperation.class.getName(),
XmlUtil.toString(message));
return NetconfOperationExecution.createExecutionChain(sortedByPriority, sortedByPriority.lastKey());
}
private TreeMap<HandlingPriority, NetconfOperation> getSortedNetconfOperationsWithCanHandle(Document message,
- NetconfSession session) {
+ NetconfServerSession session) {
TreeMap<HandlingPriority, NetconfOperation> sortedPriority = Maps.newTreeMap();
for (NetconfOperation netconfOperation : allNetconfOperations) {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- logger.warn("An exception occured during message handling", cause);
+ logger.warn("An exception occurred during message handling", cause);
handleDeserializerException(ctx, cause);
}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <capabilities>
- <capability>urn:ietf:params:netconf:base:1.0</capability>
- </capabilities>
- <session-id>1</session-id>
-</hello>
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.netconf.client.test.TestingNetconfClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
import org.opendaylight.controller.netconf.StubUserManager;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.client.test.TestingNetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import java.util.concurrent.TimeoutException;
import static java.util.Collections.emptyList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
}
static Collection<InputStream> getBasicYangs() throws IOException {
+
List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
"/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", "/META-INF/yang/test-types.yang",
"/META-INF/yang/ietf-inet-types.yang");
}
}
- /*
- @Test
- public void testStartExi() throws Exception {
- try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) {
-
-
- Document rpcReply = netconfClient.sendMessage(this.startExi)
- .getDocument();
- assertIsOK(rpcReply);
-
- ExiParameters exiParams = new ExiParameters();
- exiParams.setParametersFromXmlElement(XmlElement.fromDomDocument(this.startExi.getDocument()));
-
- netconfClient.getClientSession().addExiDecoder(ExiDecoderHandler.HANDLER_NAME, new ExiDecoderHandler(exiParams));
- netconfClient.getClientSession().addExiEncoder(ExiEncoderHandler.HANDLER_NAME, new ExiEncoderHandler(exiParams));
-
- rpcReply = netconfClient.sendMessage(this.editConfig)
- .getDocument();
- assertIsOK(rpcReply);
-
- rpcReply = netconfClient.sendMessage(this.stopExi)
- .getDocument();
- assertIsOK(rpcReply);
-
- }
- }
- */
-
@Test
public void testCloseSession() throws Exception {
try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) {
}.join();
}
-
}
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.collect.Sets;
-import static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithText;
import io.netty.channel.ChannelFuture;
import junit.framework.Assert;
import org.junit.Before;
import org.mockito.Mock;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.netconf.client.test.TestingNetconfClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
-import org.opendaylight.controller.netconf.client.test.TestingNetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithText;
public class NetconfMonitoringITTest extends AbstractNetconfConfigTest {
}
- @Test(timeout = 5 * 10000)
+ @Test(timeout = 13 * 10000)
public void testClientHelloWithAuth() throws Exception {
String fileName = "netconfMessages/client_hello_with_auth.xml";
String hello = XmlFileLoader.fileToString(fileName);
mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(),
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(),
- mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject());
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.thirdparty", "nagasena-rta").versionAsInProject());
+
+
}
private Option testingModules() {
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>nagasena</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena-rta</artifactId>
+ </dependency>
</dependencies>
<build>
org.xml.sax,
org.xml.sax.helpers,
org.opendaylight.controller.config.api,
+ org.openexi.*,
</Import-Package>
</instructions>
</configuration>
--- /dev/null
+/*
+ * Copyright (c) 2013 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util;
+
+import java.io.IOException;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfSessionListener;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.handler.NetconfEXICodec;
+import org.opendaylight.controller.netconf.util.xml.EXIParameters;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.AbstractProtocolSession;
+import org.openexi.proc.common.EXIOptionsException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+
+public abstract class AbstractNetconfSession<S extends NetconfSession, L extends NetconfSessionListener<S>> extends AbstractProtocolSession<NetconfMessage> implements NetconfSession, NetconfExiSession {
+ private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSession.class);
+ private final L sessionListener;
+ private final long sessionId;
+ private boolean up = false;
+
+ private ChannelHandler delayedEncoder;
+
+ private final Channel channel;
+
+ protected AbstractNetconfSession(final L sessionListener, final Channel channel, final long sessionId) {
+ this.sessionListener = sessionListener;
+ this.channel = channel;
+ this.sessionId = sessionId;
+ logger.debug("Session {} created", sessionId);
+ }
+
+ protected abstract S thisInstance();
+
+ @Override
+ public void close() {
+ channel.close();
+ up = false;
+ sessionListener.onSessionTerminated(thisInstance(), new NetconfTerminationReason("Session closed"));
+ }
+
+ @Override
+ protected void handleMessage(final NetconfMessage netconfMessage) {
+ logger.debug("handling incoming message");
+ sessionListener.onMessage(thisInstance(), netconfMessage);
+ }
+
+ @Override
+ public ChannelFuture sendMessage(final NetconfMessage netconfMessage) {
+ final ChannelFuture future = channel.writeAndFlush(netconfMessage);
+ if (delayedEncoder !=null) {
+ replaceMessageEncoder(delayedEncoder);
+ delayedEncoder = null;
+ }
+
+ return future;
+ }
+
+ @Override
+ protected void endOfInput() {
+ logger.debug("Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
+ : "initialized");
+ if (isUp()) {
+ this.sessionListener.onSessionDown(thisInstance(), new IOException("End of input detected. Close the session."));
+ }
+ }
+
+ @Override
+ protected void sessionUp() {
+ logger.debug("Session {} up", toString());
+ sessionListener.onSessionUp(thisInstance());
+ this.up = true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer(getClass().getSimpleName() + "{");
+ sb.append("sessionId=").append(sessionId);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ protected <T extends ChannelHandler> T removeHandler(final Class<T> handlerType) {
+ return this.channel.pipeline().remove(handlerType);
+ }
+
+ protected void replaceMessageDecoder(final ChannelHandler handler) {
+ replaceChannelHandler(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, handler);
+ }
+
+ protected void replaceMessageEncoder(final ChannelHandler handler) {
+ replaceChannelHandler(AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, handler);
+ }
+
+ protected void replaceMessageEncoderAfterNextMessage(final ChannelHandler handler) {
+ this.delayedEncoder = handler;
+ }
+
+ protected void replaceChannelHandler(final String handlerName, final ChannelHandler handler) {
+ channel.pipeline().replace(handlerName, handlerName, handler);
+ }
+
+ @Override
+ public final void startExiCommunication(final NetconfMessage startExiMessage) {
+ final EXIParameters exiParams;
+ try {
+ exiParams = EXIParameters.fromXmlElement(XmlElement.fromDomDocument(startExiMessage.getDocument()));
+ } catch (final EXIOptionsException e) {
+ logger.warn("Unable to parse EXI parameters from {} om session {}", XmlUtil.toString(startExiMessage.getDocument()), this, e);
+ throw new IllegalArgumentException(e);
+ }
+ final NetconfEXICodec exiCodec = new NetconfEXICodec(exiParams.getOptions());
+ addExiHandlers(exiCodec);
+ logger.debug("EXI handlers added to pipeline on session {}", this);
+ }
+
+ protected abstract void addExiHandlers(NetconfEXICodec exiCodec);
+
+ public final boolean isUp() {
+ return up;
+ }
+
+ public final long getSessionId() {
+ return sessionId;
+ }
+}
package org.opendaylight.controller.netconf.util;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
-
-import java.util.concurrent.TimeUnit;
-
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import org.opendaylight.controller.netconf.api.AbstractNetconfSession;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSessionListener;
import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import java.util.concurrent.TimeUnit;
public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionPreferences, S extends AbstractNetconfSession<S, L>, L extends NetconfSessionListener<S>>
extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class);
+
public static final String NAME_OF_EXCEPTION_HANDLER = "lastExceptionHandler";
- private final P sessionPreferences;
+ protected final P sessionPreferences;
private final L sessionListener;
private Timeout timeout;
/**
* Possible states for Finite State Machine
*/
- private enum State {
+ protected enum State {
IDLE, OPEN_WAIT, FAILED, ESTABLISHED
}
private final Timer timer;
private final long connectionTimeoutMillis;
+ // TODO shrink constructor
protected AbstractNetconfSessionNegotiator(P sessionPreferences, Promise<S> promise, Channel channel, Timer timer,
L sessionListener, long connectionTimeoutMillis) {
super(promise, channel);
sendMessage((NetconfHelloMessage)helloMessage);
changeState(State.OPEN_WAIT);
}
-
private void cancelTimeout() {
if(timeout!=null) {
timeout.cancel();
@Override
protected void handleMessage(NetconfHelloMessage netconfMessage) {
- Preconditions.checkNotNull(netconfMessage != null, "netconfMessage");
+ S session = getSessionForHelloMessage(netconfMessage);
+ negotiationSuccessful(session);
+ }
+
+ protected final S getSessionForHelloMessage(NetconfHelloMessage netconfMessage) {
+ Preconditions.checkNotNull(netconfMessage, "netconfMessage");
final Document doc = netconfMessage.getDocument();
}
changeState(State.ESTABLISHED);
- S session = getSession(sessionListener, channel, netconfMessage);
-
- negotiationSuccessful(session);
+ return getSession(sessionListener, channel, netconfMessage);
}
/**
* Insert chunk framing handlers into the pipeline
*/
- private void insertChunkFramingToPipeline() {
+ protected void insertChunkFramingToPipeline() {
replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER,
FramingMechanismHandlerFactory.createHandler(FramingMechanism.CHUNK));
replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR,
new NetconfChunkAggregator());
}
- private boolean shouldUseChunkFraming(Document doc) {
+ protected boolean shouldUseChunkFraming(Document doc) {
return containsBase11Capability(doc)
&& containsBase11Capability(sessionPreferences.getHelloMessage().getDocument());
}
/**
* Remove special handlers for hello message. Insert regular netconf xml message (en|de)coders.
*/
- private void replaceHelloMessageHandlers() {
+ protected void replaceHelloMessageHandlers() {
replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, new NetconfXMLToMessageDecoder());
replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, new NetconfMessageToXMLEncoder());
}
protected abstract S getSession(L sessionListener, Channel channel, NetconfHelloMessage message);
- private synchronized void changeState(final State newState) {
+ protected synchronized void changeState(final State newState) {
logger.debug("Changing state from : {} to : {}", state, newState);
Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
newState);
if (state == State.OPEN_WAIT && newState == State.FAILED) {
return true;
}
-
logger.debug("Transition from {} to {} is not allowed", state, newState);
return false;
}
* Handler to catch exceptions in pipeline during negotiation
*/
private final class ExceptionHandlingInboundChannelHandler extends ChannelInboundHandlerAdapter {
-
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.warn("An exception occurred during negotiation on channel {}", channel.localAddress(), cause);
--- /dev/null
+/*
+ * Copyright (c) 2013 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+
+/**
+ * Session capable of exi communication according to http://tools.ietf.org/html/draft-varga-netconf-exi-capability-02
+ */
+public interface NetconfExiSession {
+
+ /**
+ * Start exi communication with parameters included in start-exi message
+ */
+ void startExiCommunication(NetconfMessage startExiMessage);
+
+ /**
+ * Stop exi communication, initiated by stop-exi message
+ */
+ void stopExiCommunication();
+}
package org.opendaylight.controller.netconf.util.handler;
+import com.google.common.base.Preconditions;
import org.openexi.proc.HeaderOptionsOutputType;
-import org.openexi.proc.common.AlignmentType;
import org.openexi.proc.common.EXIOptions;
import org.openexi.proc.common.EXIOptionsException;
import org.openexi.proc.common.GrammarOptions;
import org.openexi.sax.EXIReader;
import org.openexi.sax.Transmogrifier;
-import com.google.common.base.Preconditions;
-
-final class NetconfEXICodec {
+public final class NetconfEXICodec {
/**
* NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it
* decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head
* of the stream. This is really useful, so let's output it now.
*/
private static final boolean OUTPUT_EXI_COOKIE = true;
- private final AlignmentType alignmentType;
private final EXIOptions exiOptions;
- public NetconfEXICodec(final AlignmentType alignmentType, final EXIOptions exiOptions) {
- this.alignmentType = Preconditions.checkNotNull(alignmentType);
+ public NetconfEXICodec(final EXIOptions exiOptions) {
this.exiOptions = Preconditions.checkNotNull(exiOptions);
}
Transmogrifier getTransmogrifier() throws EXIOptionsException {
final Transmogrifier transmogrifier = new Transmogrifier();
- transmogrifier.setAlignmentType(alignmentType);
+ transmogrifier.setAlignmentType(exiOptions.getAlignmentType());
transmogrifier.setBlockSize(exiOptions.getBlockSize());
transmogrifier.setGrammarCache(getGrammarCache());
transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE);
*/
package org.opendaylight.controller.netconf.util.handler;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufInputStream;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-
import java.io.InputStream;
import java.util.List;
+import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.openexi.sax.EXIReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder {
- private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
- // FIXME: is this needed?
- // private static final SAXParserFactory saxParserFactory;
- // static {
- // saxParserFactory = SAXParserFactory.newInstance();
- // saxParserFactory.setNamespaceAware(true);
- // }
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfEXIToMessageDecoder.class);
+
+// private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
private final NetconfEXICodec codec;
}
@Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+ protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) throws Exception {
/*
* Note that we could loop here and process all the messages, but we can't do that.
* The reason is <stop-exi> operation, which has the contract of immediately stopping
* the use of EXI, which means the next message needs to be decoded not by us, but rather
* by the XML decoder.
*/
- final DOMResult result = new DOMResult();
+ // If empty Byte buffer is passed to r.parse, EOFException is thrown
+
+ if (in.readableBytes() == 0) {
+ LOG.debug("No more content in incoming buffer.");
+ return;
+ }
+
+ LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in));
+
final EXIReader r = codec.getReader();
- final TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();
- transformerHandler.setResult(result);
- r.setContentHandler(transformerHandler);
+ final SAXTransformerFactory transformerFactory
+ = (SAXTransformerFactory) TransformerFactory.newInstance();
+ final TransformerHandler handler = transformerFactory.newTransformerHandler();
+ r.setContentHandler(handler);
+
+ final DOMResult domResult = new DOMResult();
+ handler.setResult(domResult);
+
try (final InputStream is = new ByteBufInputStream(in)) {
r.parse(new InputSource(is));
}
- out.add(new NetconfMessage((Document) result.getNode()));
+
+ out.add(new NetconfMessage((Document) domResult.getNode()));
}
}
*/
package org.opendaylight.controller.netconf.util.handler;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufOutputStream;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-
+import java.io.ByteArrayInputStream;
import java.io.OutputStream;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.openexi.sax.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
public final class NetconfMessageToEXIEncoder extends MessageToByteEncoder<NetconfMessage> {
- private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageToEXIEncoder.class);
+
+ //private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
private final NetconfEXICodec codec;
public NetconfMessageToEXIEncoder(final NetconfEXICodec codec) {
}
@Override
- protected void encode(ChannelHandlerContext ctx, NetconfMessage msg, ByteBuf out) throws Exception {
+ protected void encode(final ChannelHandlerContext ctx, final NetconfMessage msg, final ByteBuf out) throws Exception {
+ LOG.trace("Sent to encode : {}", XmlUtil.toString(msg.getDocument()));
+
try (final OutputStream os = new ByteBufOutputStream(out)) {
final Transmogrifier transmogrifier = codec.getTransmogrifier();
transmogrifier.setOutputStream(os);
- final Transformer transformer = saxTransformerFactory.newTransformer();
- transformer.transform(new DOMSource(msg.getDocument()), new SAXResult(transmogrifier.getSAXTransmogrifier()));
+ // FIXME transformer not working, see EXILibTest
+ transmogrifier.encode(new InputSource(new ByteArrayInputStream(XmlUtil.toString(msg.getDocument()).getBytes())));
+ //final Transformer transformer = saxTransformerFactory.newTransformer();
+ //transformer.transform(new DOMSource(msg.getDocument()), new SAXResult(transmogrifier.getSAXTransmogrifier()));
}
}
}
import javax.xml.transform.stream.StreamResult;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Comment;
@Override
@VisibleForTesting
public void encode(ChannelHandlerContext ctx, NetconfMessage msg, ByteBuf out) throws IOException, TransformerException {
- LOG.debug("Sent to encode : {}", msg);
+ LOG.trace("Sent to encode : {}", XmlUtil.toString(msg.getDocument()));
if (clientId.isPresent()) {
Comment comment = msg.getDocument().createComment("clientId:" + clientId.get());
return canHandle(operationNameAndNamespace.getOperationName(), operationNameAndNamespace.getNamespace());
}
- public static class OperationNameAndNamespace {
+ public static final class OperationNameAndNamespace {
private final String operationName, namespace;
public OperationNameAndNamespace(Document message) {
package org.opendaylight.controller.netconf.util.messages;
+import java.util.Set;
+
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
/**
* NetconfMessage that can carry additional header with session metadata. See {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader}
XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlUtil.toString(doc)), e);
}
}
+
+ public static NetconfHelloMessage createClientHello(Iterable<String> capabilities,
+ Optional<NetconfHelloMessageAdditionalHeader> additionalHeaderOptional) {
+ Document doc = createHelloMessageDoc(capabilities);
+ return additionalHeaderOptional.isPresent() ? new NetconfHelloMessage(doc, additionalHeaderOptional.get())
+ : new NetconfHelloMessage(doc);
+ }
+
+ private static Document createHelloMessageDoc(Iterable<String> capabilities) {
+ Document doc = XmlUtil.newDocument();
+ Element helloElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ HELLO_TAG);
+ Element capabilitiesElement = doc.createElement(XmlNetconfConstants.CAPABILITIES);
+
+ for (String capability : Sets.newHashSet(capabilities)) {
+ Element capElement = doc.createElement(XmlNetconfConstants.CAPABILITY);
+ capElement.setTextContent(capability);
+ capabilitiesElement.appendChild(capElement);
+ }
+
+ helloElement.appendChild(capabilitiesElement);
+
+ doc.appendChild(helloElement);
+ return doc;
+ }
+
+ public static NetconfHelloMessage createServerHello(Set<String> capabilities, long sessionId) {
+ Document doc = createHelloMessageDoc(capabilities);
+ Element sessionIdElement = doc.createElement(XmlNetconfConstants.SESSION_ID);
+ sessionIdElement.setTextContent(Long.toString(sessionId));
+ doc.getDocumentElement().appendChild(sessionIdElement);
+ return new NetconfHelloMessage(doc);
+ }
}
package org.opendaylight.controller.netconf.util.messages;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.w3c.dom.Document;
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+
public final class NetconfMessageUtil {
private NetconfMessageUtil() {}
}
public static boolean isOKMessage(XmlElement xmlElement) {
+ if(xmlElement.getChildElements().size() != 1) {
+ return false;
+ }
return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.OK);
}
- public static boolean isErrorMEssage(NetconfMessage message) {
+ public static boolean isErrorMessage(NetconfMessage message) {
return isErrorMessage(message.getDocument());
}
}
public static boolean isErrorMessage(XmlElement xmlElement) {
+ if(xmlElement.getChildElements().size() != 1) {
+ return false;
+ }
return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.RPC_ERROR);
+ }
+
+ public static Collection<String> extractCapabilitiesFromHello(Document doc) {
+ XmlElement responseElement = XmlElement.fromDomDocument(doc);
+ XmlElement capabilitiesElement = responseElement
+ .getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CAPABILITIES);
+ List<XmlElement> caps = capabilitiesElement.getChildElements(XmlNetconfConstants.CAPABILITY);
+ return Collections2.transform(caps, new Function<XmlElement, String>() {
+
+ @Nullable
+ @Override
+ public String apply(@Nullable XmlElement input) {
+ // Trim possible leading/tailing whitespace
+ return input.getTextContent().trim();
+ }
+ });
}
}
--- /dev/null
+/*
+ * Copyright (c) 2013 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.util.messages;
+
+import java.util.List;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.openexi.proc.common.EXIOptions;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Start-exi netconf message.
+ */
+public final class NetconfStartExiMessage extends NetconfMessage {
+
+ public static final String START_EXI = "start-exi";
+ public static final String ALIGNMENT_KEY = "alignment";
+ public static final String FIDELITY_KEY = "fidelity";
+ public static final String COMMENTS_KEY = "comments";
+ public static final String DTD_KEY = "dtd";
+ public static final String LEXICAL_VALUES_KEY = "lexical-values";
+ public static final String PIS_KEY = "pis";
+ public static final String PREFIXES_KEY = "prefixes";
+
+ private NetconfStartExiMessage(Document doc) {
+ super(doc);
+ }
+
+ public static NetconfStartExiMessage create(EXIOptions exiOptions, String messageId) {
+ Document doc = XmlUtil.newDocument();
+ Element rpcElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ XmlNetconfConstants.RPC_KEY);
+ rpcElement.setAttributeNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+ XmlNetconfConstants.MESSAGE_ID, messageId);
+
+ // TODO draft http://tools.ietf.org/html/draft-varga-netconf-exi-capability-02#section-3.5.1 has no namespace for start-exi element in xml
+ Element startExiElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0,
+ START_EXI);
+
+ addAlignemnt(exiOptions, doc, startExiElement);
+ addFidelity(exiOptions, doc, startExiElement);
+
+ rpcElement.appendChild(startExiElement);
+
+ doc.appendChild(rpcElement);
+ return new NetconfStartExiMessage(doc);
+ }
+
+ private static void addFidelity(EXIOptions exiOptions, Document doc, Element startExiElement) {
+ List<Element> fidelityElements = Lists.newArrayList();
+ createFidelityElement(doc, fidelityElements, exiOptions.getPreserveComments(), COMMENTS_KEY);
+ createFidelityElement(doc, fidelityElements, exiOptions.getPreserveDTD(), DTD_KEY);
+ createFidelityElement(doc, fidelityElements, exiOptions.getPreserveLexicalValues(), LEXICAL_VALUES_KEY);
+ createFidelityElement(doc, fidelityElements, exiOptions.getPreservePIs(), PIS_KEY);
+ createFidelityElement(doc, fidelityElements, exiOptions.getPreserveNS(), PREFIXES_KEY);
+
+ if (fidelityElements.isEmpty() == false) {
+ Element fidelityElement = doc.createElementNS(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0, FIDELITY_KEY);
+ for (Element element : fidelityElements) {
+ fidelityElement.appendChild(element);
+ }
+ startExiElement.appendChild(fidelityElement);
+ }
+ }
+
+ private static void addAlignemnt(EXIOptions exiOptions, Document doc, Element startExiElement) {
+ Element alignmentElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0,
+ ALIGNMENT_KEY);
+ alignmentElement.setTextContent(exiOptions.getAlignmentType().toString());
+ startExiElement.appendChild(alignmentElement);
+ }
+
+ private static void createFidelityElement(Document doc, List<Element> fidelityElements, boolean fidelity, String fidelityName) {
+
+ if (fidelity) {
+ fidelityElements.add(doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0,
+ fidelityName));
+ }
+
+ }
+}
*/
package org.opendaylight.controller.netconf.util.xml;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.openexi.proc.common.AlignmentType;
import org.openexi.proc.common.EXIOptions;
import org.openexi.proc.common.EXIOptionsException;
this.options = Preconditions.checkNotNull(options);
}
- public static EXIParameters forXmlElement(final XmlElement root) throws EXIOptionsException {
+ public static EXIParameters fromNetconfMessage(final NetconfMessage root) throws EXIOptionsException {
+ return fromXmlElement(XmlElement.fromDomDocument(root.getDocument()));
+ }
+
+ public static EXIParameters fromXmlElement(final XmlElement root) throws EXIOptionsException {
final EXIOptions options = new EXIOptions();
options.setAlignmentType(AlignmentType.bitPacked);
}
if (root.getElementsByTagName(EXI_PARAMETER_SCHEMA).getLength() > 0) {
+/*
+ GrammarFactory grammarFactory = GrammarFactory.newInstance();
+ if (operationElement
+ .getElementsByTagName(EXI_PARAMETER_SCHEMA_NONE)
+ .getLength() > 0) {
+ this.grammars = grammarFactory.createSchemaLessGrammars();
+ }
+
+ if (operationElement.getElementsByTagName(
+ EXI_PARAMETER_SCHEMA_BUILT_IN).getLength() > 0) {
+ this.grammars = grammarFactory.createXSDTypesOnlyGrammars();
+ }
- // GrammarFactory grammarFactory = GrammarFactory.newInstance();
- // if (operationElement
- // .getElementsByTagName(EXI_PARAMETER_SCHEMA_NONE)
- // .getLength() > 0) {
- // this.grammars = grammarFactory.createSchemaLessGrammars();
- // }
- //
- // if (operationElement.getElementsByTagName(
- // EXI_PARAMETER_SCHEMA_BUILT_IN).getLength() > 0) {
- // this.grammars = grammarFactory.createXSDTypesOnlyGrammars();
- // }
- //
- // if (operationElement.getElementsByTagName(
- // EXI_PARAMETER_SCHEMA_BASE_1_1).getLength() > 0) {
- // this.grammars = grammarFactory
- // .createGrammars(NETCONF_XSD_LOCATION);
- // }
+ if (operationElement.getElementsByTagName(
+ EXI_PARAMETER_SCHEMA_BASE_1_1).getLength() > 0) {
+ this.grammars = grammarFactory
+ .createGrammars(NETCONF_XSD_LOCATION);
+ }
+*/
}
public final class XmlNetconfConstants {
+
+
private XmlNetconfConstants() {}
public static final String MOUNTPOINTS = "mountpoints";
public static final String PREFIX = "prefix";
+ public static final String MESSAGE_ID = "message-id";
+ public static final String SESSION_ID = "session-id";
+
//
public static final String URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0";
+ public static final String URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_1 = "urn:ietf:params:xml:ns:netconf:base:1.1";
+ public static final String URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0 = "urn:ietf:params:xml:ns:netconf:exi:1.0";
+
+ public static final String URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0 = "urn:ietf:params:netconf:capability:exi:1.0";
public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
// TODO where to store namespace of config ?
public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "urn:opendaylight:params:xml:ns:yang:controller:config";
--- /dev/null
+/*
+ * Copyright (c) 2014 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.StringWriter;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openexi.proc.common.AlignmentType;
+import org.openexi.proc.common.GrammarOptions;
+import org.openexi.proc.grammars.GrammarCache;
+import org.openexi.sax.EXIReader;
+import org.openexi.sax.Transmogrifier;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+/**
+ * This test case tests nagasena library used for exi encode/decode.
+ *
+ * This library does not work correctly, since it is impossible to encode and then decode DOM xml.
+ * Encoding DOM using sax Transformer produces invalid xml, that cannot be decoded (Problem seems to be the namespace handling).
+ *
+ */
+@Ignore
+public class EXILibTest {
+
+ public static final AlignmentType ALIGNMENT_TYPE = AlignmentType.preCompress;
+
+ @Test
+ public void testExiLibWithSaxTransformer() throws Exception {
+ final byte[] encode = encodeEXI(getDom2());
+ final byte[] encodeWithTransformer = encodeEXITransformer(getDom2());
+
+ // System.err.println(Arrays.toString(encode));
+ // System.err.println(Arrays.toString(encodeWithTransformer));
+
+ // This works fine (encoded from string)
+ decodeEXI(encode);
+ // Error, encoded from Dom with Transformer cannot be decoded, Exception is thrown
+ //
+ // either:
+ // org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
+ //
+ // or:
+ // java.lang.NullPointerException
+ //
+ // depends on GrammarOptions.addNS(go); option set
+ decodeEXI(encodeWithTransformer);
+ }
+
+ private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ public static byte[] encodeEXITransformer(final Element xml) throws Exception {
+ final Transmogrifier transmogrifier = new Transmogrifier();
+
+ transmogrifier.setAlignmentType(ALIGNMENT_TYPE);
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ transmogrifier.setGrammarCache(getGrammarCache());
+
+ transmogrifier.setOutputStream(out);
+
+ final Transformer transformer = saxTransformerFactory.newTransformer();
+ transformer.transform(new DOMSource(xml), new SAXResult(transmogrifier.getSAXTransmogrifier()));
+
+ return out.toByteArray();
+ }
+
+ public static byte[] encodeEXI(final Element xml) throws Exception {
+ final Transmogrifier transmogrifier = new Transmogrifier();
+
+ transmogrifier.setAlignmentType(ALIGNMENT_TYPE);
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ transmogrifier.setGrammarCache(getGrammarCache());
+
+ transmogrifier.setOutputStream(out);
+
+ transmogrifier.encode(new InputSource(new ByteArrayInputStream(toString(xml, false).getBytes())));
+
+ out.flush();
+
+ return out.toByteArray();
+ }
+
+ private static GrammarCache getGrammarCache() {
+ short go = GrammarOptions.DEFAULT_OPTIONS;
+
+ // This option on or off, nagasena still fails
+// go = GrammarOptions.addNS(go);
+
+ return new GrammarCache(null, go);
+ }
+
+ public static Document decodeEXI(final byte[] input) throws Exception {
+
+ final GrammarCache grammarCache;
+ final DOMResult domResult = new DOMResult();
+
+ try(ByteArrayInputStream in = new ByteArrayInputStream(input)) {
+
+ final EXIReader reader = new EXIReader();
+
+ reader.setAlignmentType(ALIGNMENT_TYPE);
+ grammarCache = getGrammarCache();
+
+ reader.setGrammarCache(grammarCache);
+
+ final SAXTransformerFactory transformerFactory
+ = (SAXTransformerFactory) TransformerFactory.newInstance();
+ final TransformerHandler handler = transformerFactory.newTransformerHandler();
+ handler.setResult(domResult);
+
+ reader.setContentHandler(handler);
+
+ reader.parse(new InputSource(in));
+ }
+
+ return (Document) domResult.getNode();
+ }
+
+ public static Element getDom() {
+ final Element dom;
+
+ final Document d = newDocument();
+
+ dom = d.createElement("rpc");
+ dom.setAttribute("xmlns", "a.b.c");
+ dom.setAttribute("message-id", "id");
+ dom.appendChild(d.createElement("inner"));
+
+ return dom;
+ }
+
+ public static Element getDom2() {
+ final Element dom;
+
+ final Document d = newDocument();
+
+ dom = d.createElementNS("a.b.c", "rpc");
+ dom.setAttribute("message-id", "id");
+ dom.appendChild(d.createElement("inner"));
+
+ return dom;
+ }
+
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ BUILDERFACTORY = factory;
+ }
+
+ private static Document newDocument() {
+ try {
+ final DocumentBuilder builder = BUILDERFACTORY.newDocumentBuilder();
+ return builder.newDocument();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException("Failed to create document", e);
+ }
+ }
+
+ private static String toString(final Element xml, final boolean addXmlDeclaration) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, addXmlDeclaration ? "no" : "yes");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+}
-<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" message-id="a">
-<start-exi>
-<alignment>pre-compression</alignment>
-<fidelity>
-<dtd/>
-<lexical-values/>
-</fidelity>
-</start-exi>
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" message-id="a">\r
+<start-exi xmlns="urn:ietf:params:xml:ns:netconf:exi:1.0">\r
+<alignment>pre-compression</alignment>\r
+<fidelity>\r
+<dtd/>\r
+<lexical-values/>\r
+</fidelity>\r
+</start-exi>\r
</rpc>
\ No newline at end of file
-<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <stop-exi/>
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">\r
+ <stop-exi xmlns="urn:ietf:params:xml:ns:netconf:exi:1.0"/>\r
</rpc>
\ No newline at end of file