001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.thrift;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022
023import java.net.InetAddress;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.List;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.testclassification.ClientTests;
030import org.apache.hadoop.hbase.testclassification.LargeTests;
031import org.apache.hadoop.hbase.thrift.ThriftServerRunner.ImplType;
032import org.apache.hadoop.hbase.thrift.generated.Hbase;
033import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
034import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
035import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge;
036import org.apache.thrift.protocol.TBinaryProtocol;
037import org.apache.thrift.protocol.TCompactProtocol;
038import org.apache.thrift.protocol.TProtocol;
039import org.apache.thrift.server.TServer;
040import org.apache.thrift.transport.TFramedTransport;
041import org.apache.thrift.transport.TSocket;
042import org.apache.thrift.transport.TTransport;
043import org.junit.AfterClass;
044import org.junit.BeforeClass;
045import org.junit.ClassRule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.junit.runner.RunWith;
049import org.junit.runners.Parameterized;
050import org.junit.runners.Parameterized.Parameters;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
055
056/**
057 * Start the HBase Thrift server on a random port through the command-line
058 * interface and talk to it from client side.
059 */
060@Category({ClientTests.class, LargeTests.class})
061@RunWith(Parameterized.class)
062public class TestThriftServerCmdLine {
063
064  @ClassRule
065  public static final HBaseClassTestRule CLASS_RULE =
066      HBaseClassTestRule.forClass(TestThriftServerCmdLine.class);
067
068  private static final Logger LOG =
069      LoggerFactory.getLogger(TestThriftServerCmdLine.class);
070
071  private final ImplType implType;
072  private boolean specifyFramed;
073  private boolean specifyBindIP;
074  private boolean specifyCompact;
075
076  private static final HBaseTestingUtility TEST_UTIL =
077      new HBaseTestingUtility();
078
079  private Thread cmdLineThread;
080  private volatile Exception cmdLineException;
081
082  private Exception clientSideException;
083
084  private ThriftServer thriftServer;
085  private int port;
086
087  @Parameters
088  public static Collection<Object[]> getParameters() {
089    Collection<Object[]> parameters = new ArrayList<>();
090    for (ImplType implType : ImplType.values()) {
091      for (boolean specifyFramed : new boolean[] {false, true}) {
092        for (boolean specifyBindIP : new boolean[] {false, true}) {
093          if (specifyBindIP && !implType.canSpecifyBindIP) {
094            continue;
095          }
096          for (boolean specifyCompact : new boolean[] {false, true}) {
097            parameters.add(new Object[]{implType, specifyFramed,
098                specifyBindIP, specifyCompact});
099          }
100        }
101      }
102    }
103    return parameters;
104  }
105
106  public TestThriftServerCmdLine(ImplType implType, boolean specifyFramed,
107      boolean specifyBindIP, boolean specifyCompact) {
108    this.implType = implType;
109    this.specifyFramed = specifyFramed;
110    this.specifyBindIP = specifyBindIP;
111    this.specifyCompact = specifyCompact;
112    LOG.debug(getParametersString());
113  }
114
115  private String getParametersString() {
116    return "implType=" + implType + ", " +
117        "specifyFramed=" + specifyFramed + ", " +
118        "specifyBindIP=" + specifyBindIP + ", " +
119        "specifyCompact=" + specifyCompact;
120  }
121
122  @BeforeClass
123  public static void setUpBeforeClass() throws Exception {
124    TEST_UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false);
125    TEST_UTIL.startMiniCluster();
126    //ensure that server time increments every time we do an operation, otherwise
127    //successive puts having the same timestamp will override each other
128    EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge());
129  }
130
131  @AfterClass
132  public static void tearDownAfterClass() throws Exception {
133    TEST_UTIL.shutdownMiniCluster();
134    EnvironmentEdgeManager.reset();
135  }
136
137  private void startCmdLineThread(final String[] args) {
138    LOG.info("Starting HBase Thrift server with command line: " + Joiner.on(" ").join(args));
139
140    cmdLineException = null;
141    cmdLineThread = new Thread(new Runnable() {
142      @Override
143      public void run() {
144        try {
145          thriftServer.doMain(args);
146        } catch (Exception e) {
147          cmdLineException = e;
148        }
149      }
150    });
151    cmdLineThread.setName(ThriftServer.class.getSimpleName() +
152        "-cmdline");
153    cmdLineThread.start();
154  }
155
156  @Test
157  public void testRunThriftServer() throws Exception {
158    List<String> args = new ArrayList<>();
159    if (implType != null) {
160      String serverTypeOption = implType.toString();
161      assertTrue(serverTypeOption.startsWith("-"));
162      args.add(serverTypeOption);
163    }
164    port = HBaseTestingUtility.randomFreePort();
165    args.add("-" + ThriftServer.PORT_OPTION);
166    args.add(String.valueOf(port));
167    args.add("-infoport");
168    int infoPort = HBaseTestingUtility.randomFreePort();
169    args.add(String.valueOf(infoPort));
170
171    if (specifyFramed) {
172      args.add("-" + ThriftServer.FRAMED_OPTION);
173    }
174    if (specifyBindIP) {
175      args.add("-" + ThriftServer.BIND_OPTION);
176      args.add(InetAddress.getLocalHost().getHostName());
177    }
178    if (specifyCompact) {
179      args.add("-" + ThriftServer.COMPACT_OPTION);
180    }
181    args.add("start");
182
183    thriftServer = new ThriftServer(TEST_UTIL.getConfiguration());
184    startCmdLineThread(args.toArray(new String[args.size()]));
185
186    // wait up to 10s for the server to start
187    for (int i = 0; i < 100
188        && (thriftServer.serverRunner == null || thriftServer.serverRunner.tserver == null); i++) {
189      Thread.sleep(100);
190    }
191
192    Class<? extends TServer> expectedClass = implType != null ?
193        implType.serverClass : TBoundedThreadPoolServer.class;
194    assertEquals(expectedClass,
195                 thriftServer.serverRunner.tserver.getClass());
196
197    try {
198      talkToThriftServer();
199    } catch (Exception ex) {
200      clientSideException = ex;
201    } finally {
202      stopCmdLineThread();
203    }
204
205    if (clientSideException != null) {
206      LOG.error("Thrift client threw an exception. Parameters:" +
207          getParametersString(), clientSideException);
208      throw new Exception(clientSideException);
209    }
210  }
211
212  private static volatile boolean tableCreated = false;
213
214  private void talkToThriftServer() throws Exception {
215    TSocket sock = new TSocket(InetAddress.getLocalHost().getHostName(),
216        port);
217    TTransport transport = sock;
218    if (specifyFramed || implType.isAlwaysFramed) {
219      transport = new TFramedTransport(transport);
220    }
221
222    sock.open();
223    try {
224      TProtocol prot;
225      if (specifyCompact) {
226        prot = new TCompactProtocol(transport);
227      } else {
228        prot = new TBinaryProtocol(transport);
229      }
230      Hbase.Client client = new Hbase.Client(prot);
231      if (!tableCreated){
232        TestThriftServer.createTestTables(client);
233        tableCreated = true;
234      }
235      TestThriftServer.checkTableList(client);
236
237    } finally {
238      sock.close();
239    }
240  }
241
242  private void stopCmdLineThread() throws Exception {
243    LOG.debug("Stopping " + implType.simpleClassName() + " Thrift server");
244    thriftServer.stop();
245    cmdLineThread.join();
246    if (cmdLineException != null) {
247      LOG.error("Command-line invocation of HBase Thrift server threw an " +
248          "exception", cmdLineException);
249      throw new Exception(cmdLineException);
250    }
251  }
252}
253