private static final ThreadLocal<Transformer> DEFAULT_TRANSFORMER = new ThreadLocal<Transformer>() {
@Override
protected Transformer initialValue() {
- try {
- return FACTORY.newTransformer();
- } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
- throw new IllegalStateException("Unexpected error while instantiating a Transformer", e);
- }
- };
+ return createTransformer();
+ }
@Override
public void set(final Transformer value) {
throw new UnsupportedOperationException();
- };
+ }
};
private static final ThreadLocal<Transformer> PRETTY_TRANSFORMER = new ThreadLocal<Transformer>() {
@Override
protected Transformer initialValue() {
- final Transformer ret;
-
- try {
- ret = FACTORY.newTransformer();
- } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
- throw new IllegalStateException("Unexpected error while instantiating a Transformer", e);
- }
-
+ final Transformer ret = createTransformer();
ret.setOutputProperty(OutputKeys.INDENT, "yes");
ret.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
return ret;
- };
+ }
@Override
public void set(final Transformer value) {
throw new UnsupportedOperationException();
- };
+ }
};
private ThreadLocalTransformers() {
*
* @return A transformer with default configuration based on the default implementation.
*/
- public static Transformer getDefaultTransformer() {
+ static Transformer getDefaultTransformer() {
return DEFAULT_TRANSFORMER.get();
}
*
* @return A transformer with human-friendly configuration.
*/
- public static Transformer getPrettyTransformer() {
+ static Transformer getPrettyTransformer() {
return PRETTY_TRANSFORMER.get();
}
+
+ private static Transformer createTransformer() {
+ try {
+ return FACTORY.newTransformer();
+ } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
+ throw new IllegalStateException("Unexpected error while instantiating a Transformer", e);
+ }
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.nettyutil;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR;
+import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER;
+
+import com.google.common.base.Optional;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelOutboundHandler;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.Promise;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.api.NetconfDocumentedException;
+import org.opendaylight.netconf.api.NetconfSessionListener;
+import org.opendaylight.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
+import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.netconf.nettyutil.handler.ChunkedFramingMechanismEncoder;
+import org.opendaylight.netconf.nettyutil.handler.EOMFramingMechanismEncoder;
+import org.opendaylight.netconf.nettyutil.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.netconf.nettyutil.handler.NetconfChunkAggregator;
+import org.opendaylight.netconf.nettyutil.handler.NetconfEOMAggregator;
+import org.opendaylight.netconf.nettyutil.handler.NetconfXMLToHelloMessageDecoder;
+import org.opendaylight.netconf.util.messages.FramingMechanism;
+
+public class AbstractNetconfSessionNegotiatorTest {
+
+ @Mock
+ private NetconfSessionListener<TestingNetconfSession> listener;
+ @Mock
+ private Promise<TestingNetconfSession> promise;
+ @Mock
+ private SslHandler sslHandler;
+ private EmbeddedChannel channel;
+ private AbstractNetconfSessionNegotiator negotiator;
+ private NetconfHelloMessage hello;
+ private NetconfHelloMessage helloBase11;
+ private NetconfXMLToHelloMessageDecoder xmlToHello;
+ private NetconfSessionPreferences prefs;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ channel = new EmbeddedChannel();
+ xmlToHello = new NetconfXMLToHelloMessageDecoder();
+ channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, new ChannelInboundHandlerAdapter());
+ channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, xmlToHello);
+ channel.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER, FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
+ channel.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfEOMAggregator());
+ hello = NetconfHelloMessage.createClientHello(Collections.emptySet(), Optional.absent());
+ helloBase11 = NetconfHelloMessage.createClientHello(Collections.singleton(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1), Optional.absent());
+ prefs = new NetconfSessionPreferences(helloBase11);
+ doReturn(promise).when(promise).setFailure(any());
+ doReturn(promise).when(promise).setSuccess(any());
+ negotiator = new TestSessionNegotiator(prefs, promise, channel, new HashedWheelTimer(), listener, 100L);
+ }
+
+ @Test
+ public void testStartNegotiation() throws Exception {
+ negotiator.startNegotiation();
+ Assert.assertEquals(helloBase11, channel.readOutbound());
+ }
+
+ @Test
+ public void testStartNegotiationSsl() throws Exception {
+ doReturn(true).when(sslHandler).isSharable();
+ doNothing().when(sslHandler).handlerAdded(any());
+ doNothing().when(sslHandler).write(any(), any(), any());
+ final Future<EmbeddedChannel> handshakeFuture = channel.eventLoop().newSucceededFuture(channel);
+ doReturn(handshakeFuture).when(sslHandler).handshakeFuture();
+ channel.pipeline().addLast(sslHandler);
+ negotiator.startNegotiation();
+ verify(sslHandler, timeout(1000)).write(any(), eq(helloBase11), any());
+
+ }
+
+ @Test
+ public void testStartNegotiationNotEstablished() throws Exception {
+ final ChannelOutboundHandler closedDetector = Mockito.spy(new ChannelOutboundHandlerAdapter());
+ channel.pipeline().addLast("closedDetector", closedDetector);
+ doReturn(false).when(promise).isDone();
+ doReturn(false).when(promise).isCancelled();
+ negotiator.startNegotiation();
+ verify(closedDetector, timeout(2000)).close(any(), any());
+ }
+
+ @Test
+ public void testGetSessionPreferences() throws Exception {
+ Assert.assertEquals(prefs, negotiator.getSessionPreferences());
+ }
+
+ @Test
+ public void testGetSessionForHelloMessage() throws Exception {
+ negotiator.startNegotiation();
+ final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(hello);
+ Assert.assertNotNull(session);
+ Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfEOMAggregator);
+ Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER) instanceof EOMFramingMechanismEncoder);
+ }
+
+ @Test
+ public void testGetSessionForHelloMessageBase11() throws Exception {
+ negotiator.startNegotiation();
+ final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(helloBase11);
+ Assert.assertNotNull(session);
+ Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfChunkAggregator);
+ Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER) instanceof ChunkedFramingMechanismEncoder);
+ }
+
+ @Test
+ public void testReplaceHelloMessageInboundHandler() throws Exception {
+ final List<Object> out = new ArrayList<>();
+ final byte[] msg = "<rpc/>".getBytes();
+ final ByteBuf msgBuf = Unpooled.wrappedBuffer(msg);
+ final ByteBuf helloBuf = Unpooled.wrappedBuffer(XmlUtil.toString(hello.getDocument()).getBytes());
+ negotiator.startNegotiation();
+ xmlToHello.decode(null, helloBuf, out);
+ xmlToHello.decode(null, msgBuf, out);
+ final AbstractNetconfSession session = mock(AbstractNetconfSession.class);
+ doNothing().when(session).handleMessage(any());
+ negotiator.replaceHelloMessageInboundHandler(session);
+ verify(session, times(1)).handleMessage(any());
+ }
+
+ @Test
+ public void testNegotiationFail() throws Exception {
+ negotiator.startNegotiation();
+ final RuntimeException cause = new RuntimeException("failure cause");
+ channel.pipeline().fireExceptionCaught(cause);
+ verify(promise).setFailure(cause);
+ }
+
+ private static class TestSessionNegotiator extends
+ AbstractNetconfSessionNegotiator<NetconfSessionPreferences,
+ TestingNetconfSession, NetconfSessionListener<TestingNetconfSession>> {
+
+
+ TestSessionNegotiator(final NetconfSessionPreferences sessionPreferences,
+ final Promise<TestingNetconfSession> promise, final Channel channel,
+ final Timer timer,
+ final NetconfSessionListener<TestingNetconfSession> sessionListener,
+ final long connectionTimeoutMillis) {
+ super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis);
+ }
+
+ @Override
+ protected TestingNetconfSession getSession(final NetconfSessionListener sessionListener, final Channel channel,
+ final NetconfHelloMessage message) throws NetconfDocumentedException {
+ return new TestingNetconfSession(sessionListener, channel, 0L);
+ }
+
+ @Override
+ protected void handleMessage(final NetconfHelloMessage netconfHelloMessage) throws Exception {
+
+ }
+ }
+
+
+}
\ No newline at end of file
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.opendaylight.netconf.api.NetconfMessage;
-import org.opendaylight.netconf.api.NetconfSession;
import org.opendaylight.netconf.api.NetconfSessionListener;
import org.opendaylight.netconf.api.NetconfTerminationReason;
-import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
+import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
import org.openexi.proc.common.EXIOptions;
public class AbstractNetconfSessionTest {
@Mock
- private NetconfSessionListener<NetconfSession> listener;
+ private NetconfSessionListener<TestingNetconfSession> listener;
@Mock
private Channel channel;
@Mock
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doNothing().when(listener).onMessage(any(NetconfSession.class), any(NetconfMessage.class));
- doNothing().when(listener).onSessionUp(any(NetconfSession.class));
- doNothing().when(listener).onSessionDown(any(NetconfSession.class), any(Exception.class));
- doNothing().when(listener).onSessionTerminated(any(NetconfSession.class), any(NetconfTerminationReason.class));
+ doNothing().when(listener).onMessage(any(TestingNetconfSession.class), any(NetconfMessage.class));
+ doNothing().when(listener).onSessionUp(any(TestingNetconfSession.class));
+ doNothing().when(listener).onSessionDown(any(TestingNetconfSession.class), any(Exception.class));
+ doNothing().when(listener).onSessionTerminated(any(TestingNetconfSession.class), any(NetconfTerminationReason.class));
doReturn(writeFuture).when(writeFuture).addListener(any(GenericFutureListener.class));
doReturn(eventLoop).when(channel).eventLoop();
doAnswer(new Answer<Void>() {
@Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
+ public Void answer(final InvocationOnMock invocation) throws Throwable {
final Object[] args = invocation.getArguments();
final Runnable runnable = (Runnable) args[0];
runnable.run();
testingNetconfSession.sessionUp();
testingNetconfSession.close();
verify(channel).close();
- verify(listener).onSessionTerminated(any(NetconfSession.class), any(NetconfTerminationReason.class));
+ verify(listener).onSessionTerminated(any(TestingNetconfSession.class), any(NetconfTerminationReason.class));
}
@Test
verifyZeroInteractions(listener);
testingNetconfSession.sessionUp();
testingNetconfSession.endOfInput();
- verify(listener).onSessionDown(any(NetconfSession.class), any(Exception.class));
+ verify(listener).onSessionDown(any(TestingNetconfSession.class), any(Exception.class));
}
@Test
verify(channel).writeAndFlush(clientHello);
}
- private static class TestingNetconfSession extends AbstractNetconfSession<NetconfSession, NetconfSessionListener<NetconfSession>> {
-
- protected TestingNetconfSession(final NetconfSessionListener<NetconfSession> sessionListener, final Channel channel, final long sessionId) {
- super(sessionListener, channel, sessionId);
- }
-
- @Override
- protected NetconfSession thisInstance() {
- return this;
- }
-
- @Override
- protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {}
-
- @Override
- public void stopExiCommunication() {}
- }
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.nettyutil.handler;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.xml.transform.Transformer;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ThreadLocalTransformersTest {
+
+ private ExecutorService executorService;
+
+ @Before
+ public void setUp() throws Exception {
+ executorService = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void testGetDefaultTransformer() throws Exception {
+ final Transformer t1 = ThreadLocalTransformers.getDefaultTransformer();
+ final Transformer t2 = ThreadLocalTransformers.getDefaultTransformer();
+ Assert.assertSame(t1, t2);
+ final Future<Transformer> future = executorService.submit(ThreadLocalTransformers::getDefaultTransformer);
+ Assert.assertNotSame(t1, future.get());
+ }
+
+ @Test
+ public void testGetPrettyTransformer() throws Exception {
+ final Transformer t1 = ThreadLocalTransformers.getPrettyTransformer();
+ final Transformer t2 = ThreadLocalTransformers.getPrettyTransformer();
+ Assert.assertSame(t1, t2);
+ final Future<Transformer> future = executorService.submit(ThreadLocalTransformers::getPrettyTransformer);
+ Assert.assertNotSame(t1, future.get());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ executorService.shutdown();
+ }
+}
\ No newline at end of file