1   /*
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements. See the NOTICE file distributed with this
6    * work for additional information regarding copyright ownership. The ASF
7    * licenses this file to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.thrift;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.net.InetAddress;
25  import java.nio.ByteBuffer;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.LargeTests;
34  import org.apache.hadoop.hbase.thrift.ThriftServerRunner.ImplType;
35  import org.apache.hadoop.hbase.thrift.generated.Hbase;
36  import org.apache.hadoop.hbase.util.Threads;
37  import org.apache.thrift.protocol.TBinaryProtocol;
38  import org.apache.thrift.protocol.TCompactProtocol;
39  import org.apache.thrift.protocol.TProtocol;
40  import org.apache.thrift.server.TServer;
41  import org.apache.thrift.transport.TFramedTransport;
42  import org.apache.thrift.transport.TSocket;
43  import org.apache.thrift.transport.TTransport;
44  import org.junit.AfterClass;
45  import org.junit.BeforeClass;
46  import org.junit.Test;
47  import org.junit.experimental.categories.Category;
48  import org.junit.runner.RunWith;
49  import org.junit.runners.Parameterized;
50  import org.junit.runners.Parameterized.Parameters;
51  
52  import com.google.common.base.Joiner;
53  
54  /**
55   * Start the HBase Thrift server on a random port through the command-line
56   * interface and talk to it from client side.
57   */
58  @Category(LargeTests.class)
59  @RunWith(Parameterized.class)
60  public class TestThriftServerCmdLine {
61  
62    public static final Log LOG =
63        LogFactory.getLog(TestThriftServerCmdLine.class);
64  
65    private final ImplType implType;
66    private boolean specifyFramed;
67    private boolean specifyBindIP;
68    private boolean specifyCompact;
69  
70    private static final HBaseTestingUtility TEST_UTIL =
71        new HBaseTestingUtility();
72  
73    private Thread cmdLineThread;
74    private volatile Exception cmdLineException;
75  
76    private Exception clientSideException;
77  
78    private ThriftServer thriftServer;
79    private int port;
80  
81    @Parameters
82    public static Collection<Object[]> getParameters() {
83      Collection<Object[]> parameters = new ArrayList<Object[]>();
84      for (ImplType implType : ImplType.values()) {
85        for (boolean specifyFramed : new boolean[] {false, true}) {
86          for (boolean specifyBindIP : new boolean[] {false, true}) {
87            if (specifyBindIP && !implType.canSpecifyBindIP) {
88              continue;
89            }
90            for (boolean specifyCompact : new boolean[] {false, true}) {
91              parameters.add(new Object[]{implType, new Boolean(specifyFramed),
92                  new Boolean(specifyBindIP), new Boolean(specifyCompact)});
93            }
94          }
95        }
96      }
97      return parameters;
98    }
99  
100   public TestThriftServerCmdLine(ImplType implType, boolean specifyFramed,
101       boolean specifyBindIP, boolean specifyCompact) {
102     this.implType = implType;
103     this.specifyFramed = specifyFramed;
104     this.specifyBindIP = specifyBindIP;
105     this.specifyCompact = specifyCompact;
106     LOG.debug("implType=" + implType + ", " +
107         "specifyFramed=" + specifyFramed + ", " +
108         "specifyBindIP=" + specifyBindIP + ", " +
109         "specifyCompact=" + specifyCompact);
110   }
111 
112   @BeforeClass
113   public static void setUpBeforeClass() throws Exception {
114     TEST_UTIL.startMiniCluster();
115   }
116 
117   @AfterClass
118   public static void tearDownAfterClass() throws Exception {
119     TEST_UTIL.shutdownMiniCluster();
120   }
121 
122   private void startCmdLineThread(final String[] args) {
123     LOG.info("Starting HBase Thrift server with command line: " +
124         Joiner.on(" ").join(args));
125 
126     cmdLineException = null;
127     cmdLineThread = new Thread(new Runnable() {
128       @Override
129       public void run() {
130         try {
131           thriftServer.doMain(args);
132         } catch (Exception e) {
133           cmdLineException = e;
134         }
135       }
136     });
137     cmdLineThread.setName(ThriftServer.class.getSimpleName() +
138         "-cmdline");
139     cmdLineThread.start();
140   }
141 
142   @Test(timeout=120 * 1000)
143   public void testRunThriftServer() throws Exception {
144     List<String> args = new ArrayList<String>();
145     if (implType != null) {
146       String serverTypeOption = implType.toString();
147       assertTrue(serverTypeOption.startsWith("-"));
148       args.add(serverTypeOption);
149     }
150     port = HBaseTestingUtility.randomFreePort();
151     args.add("-" + ThriftServer.PORT_OPTION);
152     args.add(String.valueOf(port));
153     if (specifyFramed) {
154       args.add("-" + ThriftServer.FRAMED_OPTION);
155     }
156     if (specifyBindIP) {
157       args.add("-" + ThriftServer.BIND_OPTION);
158       args.add(InetAddress.getLocalHost().getHostName());
159     }
160     if (specifyCompact) {
161       args.add("-" + ThriftServer.COMPACT_OPTION);
162     }
163     args.add("start");
164 
165     thriftServer = new ThriftServer(TEST_UTIL.getConfiguration());
166     startCmdLineThread(args.toArray(new String[0]));
167     // wait up to 20s for the server to start
168     for (int i = 0; i < 200
169         && (thriftServer.serverRunner == null || thriftServer.serverRunner.tserver == null); i++) {
170       Thread.sleep(100);
171     }
172 
173     Class<? extends TServer> expectedClass = implType != null ?
174         implType.serverClass : TBoundedThreadPoolServer.class;
175     assertEquals(expectedClass,
176                  thriftServer.serverRunner.tserver.getClass());
177 
178     try {
179       talkToThriftServer();
180     } catch (Exception ex) {
181       clientSideException = ex;
182     } finally {
183       stopCmdLineThread();
184     }
185 
186     if (clientSideException != null) {
187       LOG.error("Thrift client threw an exception", clientSideException);
188       throw new Exception(clientSideException);
189     }
190   }
191 
192   private void talkToThriftServer() throws Exception {
193     TSocket sock = new TSocket(InetAddress.getLocalHost().getHostName(),
194         port);
195     TTransport transport = sock;
196     if (specifyFramed || implType.isAlwaysFramed) {
197       transport = new TFramedTransport(transport);
198     }
199 
200     sock.open();
201     try {
202       TProtocol prot;
203       if (specifyCompact) {
204         prot = new TCompactProtocol(transport);
205       } else {
206         prot = new TBinaryProtocol(transport);
207       }
208       Hbase.Client client = new Hbase.Client(prot);
209       TestThriftServer.doTestTableCreateDrop(client);
210       TestThriftServer.doTestGetTableRegions(client);
211       TestThriftServer.doTestTableMutations(client);
212     } finally {
213       sock.close();
214     }
215   }
216 
217   private void stopCmdLineThread() throws Exception {
218     LOG.debug("Stopping " + implType.simpleClassName() + " Thrift server");
219     thriftServer.stop();
220     cmdLineThread.join();
221     if (cmdLineException != null) {
222       LOG.error("Command-line invocation of HBase Thrift server threw an " +
223           "exception", cmdLineException);
224       throw new Exception(cmdLineException);
225     }
226   }
227 
228 
229   @org.junit.Rule
230   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
231     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
232 }
233