/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.bookkeeper.proto; import com.google.protobuf.ByteString; import org.apache.bookkeeper.conf.ServerConfiguration; import org.apache.bookkeeper.test.BookKeeperClusterTestCase; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.protobuf.ExtensionRegistry; import org.apache.bookkeeper.auth.ClientAuthProvider; import org.apache.bookkeeper.conf.ClientConfiguration; import org.apache.bookkeeper.net.BookieSocketAddress; import org.apache.bookkeeper.util.OrderedSafeExecutor; import org.apache.bookkeeper.auth.TestAuth; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.socket.ClientSocketChannelFactory; import org.apache.bookkeeper.auth.AuthProviderFactoryFactory; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import org.apache.bookkeeper.proto.BookieProtocol.*; import org.apache.bookkeeper.proto.BookkeeperProtocol.AuthMessage; import java.util.concurrent.ArrayBlockingQueue; import static org.junit.Assert.*; public class TestBackwardCompatCMS42 extends BookKeeperClusterTestCase { static final Logger LOG = LoggerFactory.getLogger(TestBackwardCompatCMS42.class); private static final byte[] SUCCESS_RESPONSE = {1}; private static final byte[] FAILURE_RESPONSE = {2}; private static final byte[] PAYLOAD_MESSAGE = {3}; ExtensionRegistry extRegistry = ExtensionRegistry.newInstance(); ClientAuthProvider.Factory authProvider; ClientSocketChannelFactory channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); OrderedSafeExecutor executor = OrderedSafeExecutor.newBuilder().numThreads(1).name("TestBackwardCompatClient") .build(); public TestBackwardCompatCMS42() throws Exception { super(0); authProvider = AuthProviderFactoryFactory.newClientAuthProviderFactory( new ClientConfiguration()); } @Test(timeout=60000) public void testAuthSingleMessage() throws Exception { ServerConfiguration bookieConf = newServerConfiguration(); bookieConf.setBookieAuthProviderFactoryClass( TestAuth.AlwaysSucceedBookieAuthProviderFactory.class.getName()); BookieServer bookie1 = startAndStoreBookie(bookieConf); AuthMessage.Builder builder = AuthMessage.newBuilder() .setAuthPluginName(TestAuth.TEST_AUTH_PROVIDER_PLUGIN_NAME); builder.setPayload(ByteString.copyFrom(PAYLOAD_MESSAGE)); final AuthMessage authMessage = builder.build(); CompatClient42 client = newCompatClient(bookie1.getLocalAddress()); Request request = new AuthRequest(BookieProtocol.CURRENT_PROTOCOL_VERSION, authMessage); client.sendRequest(request); Response response = client.takeResponse(); assertTrue("Should be auth response", response instanceof AuthResponse); assertEquals("Should have succeeded", response.getErrorCode(), BookieProtocol.EOK); } @Test(timeout=60000) public void testAuthMultiMessage() throws Exception { ServerConfiguration bookieConf = newServerConfiguration(); bookieConf.setBookieAuthProviderFactoryClass( TestAuth.SucceedAfter3BookieAuthProviderFactory.class.getName()); BookieServer bookie1 = startAndStoreBookie(bookieConf); AuthMessage.Builder builder = AuthMessage.newBuilder() .setAuthPluginName(TestAuth.TEST_AUTH_PROVIDER_PLUGIN_NAME); builder.setPayload(ByteString.copyFrom(PAYLOAD_MESSAGE)); final AuthMessage authMessage = builder.build(); CompatClient42 client = newCompatClient(bookie1.getLocalAddress()); Request request = new AuthRequest(BookieProtocol.CURRENT_PROTOCOL_VERSION, authMessage); for (int i = 0; i < 3 ; i++) { client.sendRequest(request); Response response = client.takeResponse(); assertTrue("Should be auth response", response instanceof AuthResponse); AuthResponse authResponse = (AuthResponse)response; assertEquals("Should have succeeded", response.getErrorCode(), BookieProtocol.EOK); byte[] type = authResponse.getAuthMessage() .getPayload().toByteArray(); if (i == 2) { assertArrayEquals("Should succeed after 3", type, SUCCESS_RESPONSE); } else { assertArrayEquals("Should be payload", type, PAYLOAD_MESSAGE); } } } @Test(timeout=60000) public void testAuthFail() throws Exception { ServerConfiguration bookieConf = newServerConfiguration(); bookieConf.setBookieAuthProviderFactoryClass( TestAuth.FailAfter3BookieAuthProviderFactory.class.getName()); BookieServer bookie1 = startAndStoreBookie(bookieConf); AuthMessage.Builder builder = AuthMessage.newBuilder() .setAuthPluginName(TestAuth.TEST_AUTH_PROVIDER_PLUGIN_NAME); builder.setPayload(ByteString.copyFrom(PAYLOAD_MESSAGE)); final AuthMessage authMessage = builder.build(); CompatClient42 client = newCompatClient(bookie1.getLocalAddress()); Request request = new AuthRequest(BookieProtocol.CURRENT_PROTOCOL_VERSION, authMessage); for (int i = 0; i < 3 ; i++) { client.sendRequest(request); Response response = client.takeResponse(); assertTrue("Should be auth response", response instanceof AuthResponse); AuthResponse authResponse = (AuthResponse)response; assertEquals("Should have succeeded", response.getErrorCode(), BookieProtocol.EOK); byte[] type = authResponse.getAuthMessage() .getPayload().toByteArray(); if (i == 2) { assertArrayEquals("Should fail after 3", type, FAILURE_RESPONSE); } else { assertArrayEquals("Should be payload", type, PAYLOAD_MESSAGE); } } client.sendRequest(new ReadRequest(BookieProtocol.CURRENT_PROTOCOL_VERSION, 1L, 1L, (short)0)); Response response = client.takeResponse(); assertEquals("Should have failed", response.getErrorCode(), BookieProtocol.EUA); } // copy from TestAuth BookieServer startAndStoreBookie(ServerConfiguration conf) throws Exception { bsConfs.add(conf); BookieServer s = startBookie(conf); bs.add(s); return s; } CompatClient42 newCompatClient(BookieSocketAddress addr) throws Exception { return new CompatClient42(executor, channelFactory, addr, authProvider, extRegistry); } // extending PerChannelBookieClient to get the pipeline factory class CompatClient42 extends PerChannelBookieClient { final ArrayBlockingQueue<Response> responses = new ArrayBlockingQueue<Response>(10); final Channel channel; final CountDownLatch connected = new CountDownLatch(1); CompatClient42(OrderedSafeExecutor executor, ClientSocketChannelFactory channelFactory, BookieSocketAddress addr, ClientAuthProvider.Factory authProviderFactory, ExtensionRegistry extRegistry) throws Exception { super(executor, channelFactory, addr, authProviderFactory, extRegistry); ClientBootstrap bootstrap = new ClientBootstrap(channelFactory); bootstrap.setPipelineFactory(this); bootstrap.setOption("tcpNoDelay", false); bootstrap.setOption("keepAlive", true); ChannelFuture f = bootstrap.connect(addr.getSocketAddress()).await(); channel = f.getChannel(); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (!(e.getMessage() instanceof Response)) { LOG.error("Unknown message {}, passing upstream", e.getMessage()); ctx.sendUpstream(e); return; } responses.add((Response)e.getMessage()); } @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { connected.countDown(); } Response takeResponse() throws Exception { return responses.take(); } Response pollResponse() throws Exception { return responses.poll(); } void sendRequest(Request request) throws Exception { connected.await(); channel.write(request); } } }