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.apache.hadoop.hbase.http.HttpServerUtil.PATH_SPEC_ANY; 021import static org.apache.hadoop.hbase.thrift.Constants.BACKLOG_CONF_DEAFULT; 022import static org.apache.hadoop.hbase.thrift.Constants.BACKLOG_CONF_KEY; 023import static org.apache.hadoop.hbase.thrift.Constants.BIND_CONF_KEY; 024import static org.apache.hadoop.hbase.thrift.Constants.BIND_OPTION; 025import static org.apache.hadoop.hbase.thrift.Constants.COMPACT_CONF_DEFAULT; 026import static org.apache.hadoop.hbase.thrift.Constants.COMPACT_CONF_KEY; 027import static org.apache.hadoop.hbase.thrift.Constants.COMPACT_OPTION; 028import static org.apache.hadoop.hbase.thrift.Constants.DEFAULT_BIND_ADDR; 029import static org.apache.hadoop.hbase.thrift.Constants.DEFAULT_HTTP_MAX_HEADER_SIZE; 030import static org.apache.hadoop.hbase.thrift.Constants.DEFAULT_LISTEN_PORT; 031import static org.apache.hadoop.hbase.thrift.Constants.FRAMED_CONF_DEFAULT; 032import static org.apache.hadoop.hbase.thrift.Constants.FRAMED_CONF_KEY; 033import static org.apache.hadoop.hbase.thrift.Constants.FRAMED_OPTION; 034import static org.apache.hadoop.hbase.thrift.Constants.HTTP_MAX_THREADS_KEY; 035import static org.apache.hadoop.hbase.thrift.Constants.HTTP_MAX_THREADS_KEY_DEFAULT; 036import static org.apache.hadoop.hbase.thrift.Constants.HTTP_MIN_THREADS_KEY; 037import static org.apache.hadoop.hbase.thrift.Constants.HTTP_MIN_THREADS_KEY_DEFAULT; 038import static org.apache.hadoop.hbase.thrift.Constants.INFOPORT_OPTION; 039import static org.apache.hadoop.hbase.thrift.Constants.KEEP_ALIVE_SEC_OPTION; 040import static org.apache.hadoop.hbase.thrift.Constants.MAX_FRAME_SIZE_CONF_DEFAULT; 041import static org.apache.hadoop.hbase.thrift.Constants.MAX_FRAME_SIZE_CONF_KEY; 042import static org.apache.hadoop.hbase.thrift.Constants.MAX_QUEUE_SIZE_OPTION; 043import static org.apache.hadoop.hbase.thrift.Constants.MAX_WORKERS_OPTION; 044import static org.apache.hadoop.hbase.thrift.Constants.MIN_WORKERS_OPTION; 045import static org.apache.hadoop.hbase.thrift.Constants.PORT_CONF_KEY; 046import static org.apache.hadoop.hbase.thrift.Constants.PORT_OPTION; 047import static org.apache.hadoop.hbase.thrift.Constants.READ_TIMEOUT_OPTION; 048import static org.apache.hadoop.hbase.thrift.Constants.SELECTOR_NUM_OPTION; 049import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_FILTERS; 050import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_HTTP_ALLOW_OPTIONS_METHOD; 051import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT; 052import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_INFO_SERVER_BINDING_ADDRESS; 053import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_INFO_SERVER_BINDING_ADDRESS_DEFAULT; 054import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_INFO_SERVER_PORT; 055import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_INFO_SERVER_PORT_DEFAULT; 056import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_KERBEROS_PRINCIPAL_KEY; 057import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_KEYTAB_FILE_KEY; 058import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_QOP_KEY; 059import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SELECTOR_NUM; 060import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SERVER_SOCKET_READ_TIMEOUT_DEFAULT; 061import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SERVER_SOCKET_READ_TIMEOUT_KEY; 062import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SPNEGO_KEYTAB_FILE_KEY; 063import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SPNEGO_PRINCIPAL_KEY; 064import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_CLIENT_AUTH_MODE_KEY; 065import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_ENABLED_KEY; 066import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_EXCLUDE_CIPHER_SUITES_KEY; 067import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_EXCLUDE_PROTOCOLS_KEY; 068import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_INCLUDE_CIPHER_SUITES_KEY; 069import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_INCLUDE_PROTOCOLS_KEY; 070import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_KEYSTORE_TYPE_DEFAULT; 071import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_SERVER_TRUSTSTORE_PASSWORD_KEY; 072import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_SERVER_TRUSTSTORE_STORE_KEY; 073import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SSL_SERVER_TRUSTSTORE_TYPE_KEY; 074import static org.apache.hadoop.hbase.thrift.Constants.THRIFT_SUPPORT_PROXYUSER_KEY; 075import static org.apache.hadoop.hbase.thrift.Constants.USE_HTTP_CONF_KEY; 076 077import java.io.IOException; 078import java.net.InetAddress; 079import java.net.InetSocketAddress; 080import java.net.UnknownHostException; 081import java.security.PrivilegedAction; 082import java.util.List; 083import java.util.Map; 084import java.util.concurrent.BlockingQueue; 085import java.util.concurrent.ExecutorService; 086import java.util.concurrent.LinkedBlockingQueue; 087import java.util.concurrent.ThreadPoolExecutor; 088import java.util.concurrent.TimeUnit; 089import javax.security.auth.callback.Callback; 090import javax.security.auth.callback.UnsupportedCallbackException; 091import javax.security.sasl.AuthorizeCallback; 092import javax.security.sasl.SaslServer; 093import org.apache.commons.lang3.ArrayUtils; 094import org.apache.commons.lang3.StringUtils; 095import org.apache.hadoop.conf.Configuration; 096import org.apache.hadoop.conf.Configured; 097import org.apache.hadoop.hbase.HBaseConfiguration; 098import org.apache.hadoop.hbase.HBaseInterfaceAudience; 099import org.apache.hadoop.hbase.filter.ParseFilter; 100import org.apache.hadoop.hbase.http.HttpServerUtil; 101import org.apache.hadoop.hbase.http.InfoServer; 102import org.apache.hadoop.hbase.io.crypto.tls.TLSStore; 103import org.apache.hadoop.hbase.io.crypto.tls.X509Util; 104import org.apache.hadoop.hbase.log.HBaseMarkers; 105import org.apache.hadoop.hbase.security.SaslUtil; 106import org.apache.hadoop.hbase.security.SecurityUtil; 107import org.apache.hadoop.hbase.security.UserProvider; 108import org.apache.hadoop.hbase.thrift.generated.Hbase; 109import org.apache.hadoop.hbase.util.DNS; 110import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 111import org.apache.hadoop.hbase.util.JvmPauseMonitor; 112import org.apache.hadoop.hbase.util.VersionInfo; 113import org.apache.hadoop.security.SaslRpcServer; 114import org.apache.hadoop.security.UserGroupInformation; 115import org.apache.hadoop.security.authorize.ProxyUsers; 116import org.apache.hadoop.util.Shell.ExitCodeException; 117import org.apache.hadoop.util.Tool; 118import org.apache.hadoop.util.ToolRunner; 119import org.apache.yetus.audience.InterfaceAudience; 120import org.slf4j.Logger; 121import org.slf4j.LoggerFactory; 122 123import org.apache.hbase.thirdparty.com.google.common.base.Joiner; 124import org.apache.hbase.thirdparty.com.google.common.base.Splitter; 125import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder; 126import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 127import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLineParser; 128import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser; 129import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter; 130import org.apache.hbase.thirdparty.org.apache.commons.cli.Options; 131import org.apache.hbase.thirdparty.org.apache.thrift.TProcessor; 132import org.apache.hbase.thirdparty.org.apache.thrift.protocol.TBinaryProtocol; 133import org.apache.hbase.thirdparty.org.apache.thrift.protocol.TCompactProtocol; 134import org.apache.hbase.thirdparty.org.apache.thrift.protocol.TProtocolFactory; 135import org.apache.hbase.thirdparty.org.apache.thrift.server.THsHaServer; 136import org.apache.hbase.thirdparty.org.apache.thrift.server.TNonblockingServer; 137import org.apache.hbase.thirdparty.org.apache.thrift.server.TServer; 138import org.apache.hbase.thirdparty.org.apache.thrift.server.TServlet; 139import org.apache.hbase.thirdparty.org.apache.thrift.server.TThreadedSelectorServer; 140import org.apache.hbase.thirdparty.org.apache.thrift.transport.TNonblockingServerSocket; 141import org.apache.hbase.thirdparty.org.apache.thrift.transport.TNonblockingServerTransport; 142import org.apache.hbase.thirdparty.org.apache.thrift.transport.TSaslServerTransport; 143import org.apache.hbase.thirdparty.org.apache.thrift.transport.TServerSocket; 144import org.apache.hbase.thirdparty.org.apache.thrift.transport.TServerTransport; 145import org.apache.hbase.thirdparty.org.apache.thrift.transport.TTransportFactory; 146import org.apache.hbase.thirdparty.org.apache.thrift.transport.layered.TFramedTransport; 147import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletContextHandler; 148import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletHolder; 149import org.apache.hbase.thirdparty.org.eclipse.jetty.http.HttpVersion; 150import org.apache.hbase.thirdparty.org.eclipse.jetty.server.HttpConfiguration; 151import org.apache.hbase.thirdparty.org.eclipse.jetty.server.HttpConnectionFactory; 152import org.apache.hbase.thirdparty.org.eclipse.jetty.server.SecureRequestCustomizer; 153import org.apache.hbase.thirdparty.org.eclipse.jetty.server.Server; 154import org.apache.hbase.thirdparty.org.eclipse.jetty.server.ServerConnector; 155import org.apache.hbase.thirdparty.org.eclipse.jetty.server.SslConnectionFactory; 156import org.apache.hbase.thirdparty.org.eclipse.jetty.util.ssl.SslContextFactory; 157import org.apache.hbase.thirdparty.org.eclipse.jetty.util.thread.QueuedThreadPool; 158 159/** 160 * ThriftServer- this class starts up a Thrift server which implements the Hbase API specified in 161 * the Hbase.thrift IDL file. The server runs in an independent process. 162 */ 163@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 164public class ThriftServer extends Configured implements Tool { 165 166 private static final Logger LOG = LoggerFactory.getLogger(ThriftServer.class); 167 168 protected Configuration conf; 169 170 protected InfoServer infoServer; 171 172 protected TProcessor processor; 173 174 protected ThriftMetrics metrics; 175 protected HBaseServiceHandler hbaseServiceHandler; 176 protected UserGroupInformation serviceUGI; 177 protected UserGroupInformation httpUGI; 178 protected boolean httpEnabled; 179 180 protected SaslUtil.QualityOfProtection qop; 181 protected String host; 182 protected int listenPort; 183 184 protected boolean securityEnabled; 185 protected boolean doAsEnabled; 186 187 protected JvmPauseMonitor pauseMonitor; 188 189 protected volatile TServer tserver; 190 protected volatile Server httpServer; 191 192 // 193 // Main program and support routines 194 // 195 196 public ThriftServer(Configuration conf) { 197 this.conf = HBaseConfiguration.create(conf); 198 } 199 200 protected ThriftMetrics createThriftMetrics(Configuration conf) { 201 return new ThriftMetrics(conf, ThriftMetrics.ThriftServerType.ONE); 202 } 203 204 protected void setupParamters() throws IOException { 205 // login the server principal (if using secure Hadoop) 206 UserProvider userProvider = UserProvider.instantiate(conf); 207 securityEnabled = 208 userProvider.isHadoopSecurityEnabled() && userProvider.isHBaseSecurityEnabled(); 209 if (securityEnabled) { 210 host = DNS.getHostname(conf, DNS.ServerType.THRIFT); 211 userProvider.login(THRIFT_KEYTAB_FILE_KEY, THRIFT_KERBEROS_PRINCIPAL_KEY, host); 212 213 // Setup the SPNEGO user for HTTP if configured 214 String spnegoPrincipal = getSpengoPrincipal(conf, host); 215 String spnegoKeytab = getSpnegoKeytab(conf); 216 UserGroupInformation.setConfiguration(conf); 217 // login the SPNEGO principal using UGI to avoid polluting the login user 218 this.httpUGI = 219 UserGroupInformation.loginUserFromKeytabAndReturnUGI(spnegoPrincipal, spnegoKeytab); 220 } 221 this.serviceUGI = userProvider.getCurrent().getUGI(); 222 if (httpUGI == null) { 223 this.httpUGI = serviceUGI; 224 } 225 226 this.listenPort = conf.getInt(PORT_CONF_KEY, DEFAULT_LISTEN_PORT); 227 this.metrics = createThriftMetrics(conf); 228 this.pauseMonitor = new JvmPauseMonitor(conf, this.metrics.getSource()); 229 this.hbaseServiceHandler = createHandler(conf, userProvider); 230 this.hbaseServiceHandler.initMetrics(metrics); 231 this.processor = createProcessor(); 232 233 httpEnabled = conf.getBoolean(USE_HTTP_CONF_KEY, false); 234 doAsEnabled = conf.getBoolean(THRIFT_SUPPORT_PROXYUSER_KEY, false); 235 if (doAsEnabled && !httpEnabled) { 236 LOG.warn("Fail to enable the doAs feature. " + USE_HTTP_CONF_KEY + " is not configured"); 237 } 238 239 String strQop = conf.get(THRIFT_QOP_KEY); 240 if (strQop != null) { 241 this.qop = SaslUtil.getQop(strQop); 242 } 243 if (qop != null) { 244 if ( 245 qop != SaslUtil.QualityOfProtection.AUTHENTICATION 246 && qop != SaslUtil.QualityOfProtection.INTEGRITY 247 && qop != SaslUtil.QualityOfProtection.PRIVACY 248 ) { 249 throw new IOException(String.format("Invalid %s: It must be one of %s, %s, or %s.", 250 THRIFT_QOP_KEY, SaslUtil.QualityOfProtection.AUTHENTICATION.name(), 251 SaslUtil.QualityOfProtection.INTEGRITY.name(), 252 SaslUtil.QualityOfProtection.PRIVACY.name())); 253 } 254 checkHttpSecurity(qop, conf); 255 if (!securityEnabled) { 256 throw new IOException("Thrift server must run in secure mode to support authentication"); 257 } 258 } 259 registerFilters(conf); 260 pauseMonitor.start(); 261 } 262 263 private String getSpengoPrincipal(Configuration conf, String host) throws IOException { 264 String principal = conf.get(THRIFT_SPNEGO_PRINCIPAL_KEY); 265 if (principal == null) { 266 // We cannot use the Hadoop configuration deprecation handling here since 267 // the THRIFT_KERBEROS_PRINCIPAL_KEY config is still valid for regular Kerberos 268 // communication. The preference should be to use the THRIFT_SPNEGO_PRINCIPAL_KEY 269 // config so that THRIFT_KERBEROS_PRINCIPAL_KEY doesn't control both backend 270 // Kerberos principal and SPNEGO principal. 271 LOG.info("Using deprecated {} config for SPNEGO principal. Use {} instead.", 272 THRIFT_KERBEROS_PRINCIPAL_KEY, THRIFT_SPNEGO_PRINCIPAL_KEY); 273 principal = conf.get(THRIFT_KERBEROS_PRINCIPAL_KEY); 274 } 275 // Handle _HOST in principal value 276 return org.apache.hadoop.security.SecurityUtil.getServerPrincipal(principal, host); 277 } 278 279 private String getSpnegoKeytab(Configuration conf) { 280 String keytab = conf.get(THRIFT_SPNEGO_KEYTAB_FILE_KEY); 281 if (keytab == null) { 282 // We cannot use the Hadoop configuration deprecation handling here since 283 // the THRIFT_KEYTAB_FILE_KEY config is still valid for regular Kerberos 284 // communication. The preference should be to use the THRIFT_SPNEGO_KEYTAB_FILE_KEY 285 // config so that THRIFT_KEYTAB_FILE_KEY doesn't control both backend 286 // Kerberos keytab and SPNEGO keytab. 287 LOG.info("Using deprecated {} config for SPNEGO keytab. Use {} instead.", 288 THRIFT_KEYTAB_FILE_KEY, THRIFT_SPNEGO_KEYTAB_FILE_KEY); 289 keytab = conf.get(THRIFT_KEYTAB_FILE_KEY); 290 } 291 return keytab; 292 } 293 294 protected void startInfoServer() throws IOException { 295 // Put up info server. 296 int port = conf.getInt(THRIFT_INFO_SERVER_PORT, THRIFT_INFO_SERVER_PORT_DEFAULT); 297 298 if (port >= 0) { 299 conf.setLong("startcode", EnvironmentEdgeManager.currentTime()); 300 String a = 301 conf.get(THRIFT_INFO_SERVER_BINDING_ADDRESS, THRIFT_INFO_SERVER_BINDING_ADDRESS_DEFAULT); 302 infoServer = new InfoServer("thrift", a, port, false, conf); 303 infoServer.setAttribute("hbase.conf", conf); 304 infoServer.setAttribute("hbase.thrift.server.type", metrics.getThriftServerType().name()); 305 infoServer.start(); 306 } 307 } 308 309 protected void checkHttpSecurity(SaslUtil.QualityOfProtection qop, Configuration conf) { 310 if ( 311 qop == SaslUtil.QualityOfProtection.PRIVACY && conf.getBoolean(USE_HTTP_CONF_KEY, false) 312 && !conf.getBoolean(THRIFT_SSL_ENABLED_KEY, false) 313 ) { 314 throw new IllegalArgumentException( 315 "Thrift HTTP Server's QoP is privacy, but " + THRIFT_SSL_ENABLED_KEY + " is false"); 316 } 317 } 318 319 protected HBaseServiceHandler createHandler(Configuration conf, UserProvider userProvider) 320 throws IOException { 321 return new ThriftHBaseServiceHandler(conf, userProvider); 322 } 323 324 protected TProcessor createProcessor() { 325 return new Hbase.Processor<>( 326 HbaseHandlerMetricsProxy.newInstance((Hbase.Iface) hbaseServiceHandler, metrics, conf)); 327 } 328 329 /** 330 * the thrift server, not null means the server is started, for test only 331 * @return the tServer 332 */ 333 @InterfaceAudience.Private 334 public TServer getTserver() { 335 return tserver; 336 } 337 338 /** 339 * the Jetty server, not null means the HTTP server is started, for test only 340 * @return the http server 341 */ 342 @InterfaceAudience.Private 343 public Server getHttpServer() { 344 return httpServer; 345 } 346 347 protected void printUsageAndExit(Options options, int exitCode) throws ExitCodeException { 348 HelpFormatter formatter = new HelpFormatter(); 349 formatter.printHelp("Thrift", null, options, 350 "To start the Thrift server run 'hbase-daemon.sh start thrift' or " + "'hbase thrift'\n" 351 + "To shutdown the thrift server run 'hbase-daemon.sh stop " 352 + "thrift' or send a kill signal to the thrift server pid", 353 true); 354 System.exit(exitCode); 355 } 356 357 /** 358 * Create a Servlet for the http server 359 * @param protocolFactory protocolFactory 360 * @return the servlet 361 */ 362 protected TServlet createTServlet(TProtocolFactory protocolFactory) { 363 return new ThriftHttpServlet(processor, protocolFactory, serviceUGI, httpUGI, 364 hbaseServiceHandler, securityEnabled, doAsEnabled); 365 } 366 367 /** 368 * Setup an HTTP Server using Jetty to serve calls from THttpClient 369 * @throws IOException IOException 370 */ 371 protected void setupHTTPServer() throws IOException { 372 TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); 373 TServlet thriftHttpServlet = createTServlet(protocolFactory); 374 375 // Set the default max thread number to 100 to limit 376 // the number of concurrent requests so that Thrfit HTTP server doesn't OOM easily. 377 // Jetty set the default max thread number to 250, if we don't set it. 378 // 379 // Our default min thread number 2 is the same as that used by Jetty. 380 int minThreads = conf.getInt(HTTP_MIN_THREADS_KEY, conf 381 .getInt(TBoundedThreadPoolServer.MIN_WORKER_THREADS_CONF_KEY, HTTP_MIN_THREADS_KEY_DEFAULT)); 382 int maxThreads = conf.getInt(HTTP_MAX_THREADS_KEY, conf 383 .getInt(TBoundedThreadPoolServer.MAX_WORKER_THREADS_CONF_KEY, HTTP_MAX_THREADS_KEY_DEFAULT)); 384 QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads); 385 threadPool.setMinThreads(minThreads); 386 httpServer = new Server(threadPool); 387 388 // Context handler 389 boolean isSecure = conf.getBoolean(THRIFT_SSL_ENABLED_KEY, false); 390 ServletContextHandler ctxHandler = 391 new ServletContextHandler(httpServer, "/", ServletContextHandler.SESSIONS); 392 HttpServerUtil.addClickjackingPreventionFilter(ctxHandler, conf, PATH_SPEC_ANY); 393 HttpServerUtil.addSecurityHeadersFilter(ctxHandler, conf, isSecure, PATH_SPEC_ANY); 394 ctxHandler.addServlet(new ServletHolder(thriftHttpServlet), PATH_SPEC_ANY); 395 HttpServerUtil.constrainHttpMethods(ctxHandler, 396 conf.getBoolean(THRIFT_HTTP_ALLOW_OPTIONS_METHOD, THRIFT_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT)); 397 398 // set up Jetty and run the embedded server 399 HttpConfiguration httpConfig = new HttpConfiguration(); 400 httpConfig.setSecureScheme("https"); 401 httpConfig.setSecurePort(listenPort); 402 httpConfig.setHeaderCacheSize(DEFAULT_HTTP_MAX_HEADER_SIZE); 403 httpConfig.setRequestHeaderSize(DEFAULT_HTTP_MAX_HEADER_SIZE); 404 httpConfig.setResponseHeaderSize(DEFAULT_HTTP_MAX_HEADER_SIZE); 405 httpConfig.setSendServerVersion(false); 406 httpConfig.setSendDateHeader(false); 407 408 ServerConnector serverConnector; 409 if (isSecure) { 410 HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); 411 httpsConfig.addCustomizer(new SecureRequestCustomizer()); 412 413 SslContextFactory.Server sslCtxFactory = new SslContextFactory.Server(); 414 // Prefer the role-scoped hbase.thrift.ssl.server.* keys, falling back to the historical 415 // unscoped hbase.thrift.ssl.* keys for backward compatibility with existing deployments. A 416 // store resolves entirely from one prefix, so a role-scoped keystore is never paired with the 417 // legacy keystore's password or type. 418 TLSStore keyStore = TLSStore.resolve(conf, "hbase.thrift.ssl.server.", "hbase.thrift.ssl.", 419 TLSStore.Keys.SERVLET_KEYSTORE); 420 sslCtxFactory.setKeyStorePath(keyStore.getLocation()); 421 sslCtxFactory.setKeyStorePassword(keyStore.getPassword()); 422 sslCtxFactory.setKeyManagerPassword(keyStore.getKeyPassword()); 423 sslCtxFactory.setKeyStoreType( 424 StringUtils.defaultIfBlank(keyStore.getType(), THRIFT_SSL_KEYSTORE_TYPE_DEFAULT)); 425 426 // Truststore is entirely new for Thrift — no legacy fallback because there is no historical 427 // hbase.thrift.ssl.truststore.* configuration. Leaving it unset is only valid when client 428 // auth is NONE 429 String trustStore = conf.get(THRIFT_SSL_SERVER_TRUSTSTORE_STORE_KEY); 430 if (StringUtils.isNotBlank(trustStore)) { 431 sslCtxFactory.setTrustStorePath(trustStore); 432 String trustStorePassword = 433 HBaseConfiguration.getPassword(conf, THRIFT_SSL_SERVER_TRUSTSTORE_PASSWORD_KEY, null); 434 if (StringUtils.isNotBlank(trustStorePassword)) { 435 sslCtxFactory.setTrustStorePassword(trustStorePassword); 436 } 437 String trustStoreType = conf.get(THRIFT_SSL_SERVER_TRUSTSTORE_TYPE_KEY); 438 if (StringUtils.isNotBlank(trustStoreType)) { 439 sslCtxFactory.setTrustStoreType(trustStoreType); 440 } 441 } 442 443 // Activate mTLS if configured. Default is NONE, which preserves today's behavior of never 444 // requesting a client certificate on the Thrift-over-HTTP connector. Set 445 // hbase.thrift.ssl.server.client.auth.mode to WANT or NEED to opt in. 446 // getTrimmed + defaultIfBlank so that an empty or whitespace-only <value></value> in 447 // hbase-site.xml behaves like an absent key. A bare fromPropertyValue("") would return NEED. 448 X509Util.ClientAuth clientAuth = 449 X509Util.ClientAuth.fromPropertyValue(StringUtils.defaultIfBlank( 450 conf.getTrimmed(THRIFT_SSL_CLIENT_AUTH_MODE_KEY), X509Util.ClientAuth.NONE.name())); 451 X509Util.validateClientAuthTrustStore(clientAuth, trustStore, THRIFT_SSL_CLIENT_AUTH_MODE_KEY, 452 THRIFT_SSL_SERVER_TRUSTSTORE_STORE_KEY); 453 switch (clientAuth) { 454 case NEED: 455 sslCtxFactory.setNeedClientAuth(true); 456 break; 457 case WANT: 458 sslCtxFactory.setWantClientAuth(true); 459 break; 460 case NONE: 461 default: 462 // no-op; both flags default to false on SslContextFactory.Server 463 break; 464 } 465 466 String[] excludeCiphers = 467 conf.getStrings(THRIFT_SSL_EXCLUDE_CIPHER_SUITES_KEY, ArrayUtils.EMPTY_STRING_ARRAY); 468 if (excludeCiphers.length != 0) { 469 sslCtxFactory.setExcludeCipherSuites(excludeCiphers); 470 } 471 String[] includeCiphers = 472 conf.getStrings(THRIFT_SSL_INCLUDE_CIPHER_SUITES_KEY, ArrayUtils.EMPTY_STRING_ARRAY); 473 if (includeCiphers.length != 0) { 474 sslCtxFactory.setIncludeCipherSuites(includeCiphers); 475 } 476 477 // Disable SSLv3 by default due to "Poodle" Vulnerability - CVE-2014-3566 478 String[] excludeProtocols = conf.getStrings(THRIFT_SSL_EXCLUDE_PROTOCOLS_KEY, "SSLv3"); 479 if (excludeProtocols.length != 0) { 480 sslCtxFactory.setExcludeProtocols(excludeProtocols); 481 } 482 String[] includeProtocols = 483 conf.getStrings(THRIFT_SSL_INCLUDE_PROTOCOLS_KEY, ArrayUtils.EMPTY_STRING_ARRAY); 484 if (includeProtocols.length != 0) { 485 sslCtxFactory.setIncludeProtocols(includeProtocols); 486 } 487 488 serverConnector = new ServerConnector(httpServer, 489 new SslConnectionFactory(sslCtxFactory, HttpVersion.HTTP_1_1.toString()), 490 new HttpConnectionFactory(httpsConfig)); 491 } else { 492 serverConnector = new ServerConnector(httpServer, new HttpConnectionFactory(httpConfig)); 493 } 494 serverConnector.setPort(listenPort); 495 serverConnector.setHost(getBindAddress(conf).getHostAddress()); 496 httpServer.addConnector(serverConnector); 497 httpServer.setStopAtShutdown(true); 498 499 if (doAsEnabled) { 500 ProxyUsers.refreshSuperUserGroupsConfiguration(conf); 501 } 502 LOG.info("Starting Thrift HTTP Server on {}", Integer.toString(listenPort)); 503 } 504 505 /** 506 * Setting up the thrift TServer 507 */ 508 protected void setupServer() throws Exception { 509 // Construct correct ProtocolFactory 510 TProtocolFactory protocolFactory = getProtocolFactory(); 511 512 ImplType implType = ImplType.getServerImpl(conf); 513 TProcessor processorToUse = processor; 514 515 // Construct correct TransportFactory 516 TTransportFactory transportFactory; 517 if (conf.getBoolean(FRAMED_CONF_KEY, FRAMED_CONF_DEFAULT) || implType.isAlwaysFramed) { 518 if (qop != null) { 519 throw new RuntimeException( 520 "Thrift server authentication" + " doesn't work with framed transport yet"); 521 } 522 transportFactory = new TFramedTransport.Factory( 523 conf.getInt(MAX_FRAME_SIZE_CONF_KEY, MAX_FRAME_SIZE_CONF_DEFAULT) * 1024 * 1024); 524 LOG.debug("Using framed transport"); 525 } else if (qop == null) { 526 transportFactory = new TTransportFactory(); 527 } else { 528 // Extract the name from the principal 529 String thriftKerberosPrincipal = conf.get(THRIFT_KERBEROS_PRINCIPAL_KEY); 530 if (thriftKerberosPrincipal == null) { 531 throw new IllegalArgumentException(THRIFT_KERBEROS_PRINCIPAL_KEY + " cannot be null"); 532 } 533 String name = SecurityUtil.getUserFromPrincipal(thriftKerberosPrincipal); 534 Map<String, String> saslProperties = SaslUtil.initSaslProperties(qop.name()); 535 TSaslServerTransport.Factory saslFactory = new TSaslServerTransport.Factory(); 536 saslFactory.addServerDefinition("GSSAPI", name, host, saslProperties, 537 new SaslRpcServer.SaslGssCallbackHandler() { 538 @Override 539 public void handle(Callback[] callbacks) throws UnsupportedCallbackException { 540 AuthorizeCallback ac = null; 541 for (Callback callback : callbacks) { 542 if (callback instanceof AuthorizeCallback) { 543 ac = (AuthorizeCallback) callback; 544 } else { 545 throw new UnsupportedCallbackException(callback, 546 "Unrecognized SASL GSSAPI Callback"); 547 } 548 } 549 if (ac != null) { 550 String authid = ac.getAuthenticationID(); 551 String authzid = ac.getAuthorizationID(); 552 if (!authid.equals(authzid)) { 553 ac.setAuthorized(false); 554 } else { 555 ac.setAuthorized(true); 556 String userName = SecurityUtil.getUserFromPrincipal(authzid); 557 LOG.info("Effective user: {}", userName); 558 ac.setAuthorizedID(userName); 559 } 560 } 561 } 562 }); 563 transportFactory = saslFactory; 564 565 // Create a processor wrapper, to get the caller 566 processorToUse = (inProt, outProt) -> { 567 TSaslServerTransport saslServerTransport = (TSaslServerTransport) inProt.getTransport(); 568 SaslServer saslServer = saslServerTransport.getSaslServer(); 569 String principal = saslServer.getAuthorizationID(); 570 hbaseServiceHandler.setEffectiveUser(principal); 571 processor.process(inProt, outProt); 572 }; 573 } 574 575 if (conf.get(BIND_CONF_KEY) != null && !implType.canSpecifyBindIP) { 576 LOG.error( 577 "Server types {} don't support IP address binding at the moment. See " 578 + "https://issues.apache.org/jira/browse/HBASE-2155 for details.", 579 Joiner.on(", ").join(ImplType.serversThatCannotSpecifyBindIP())); 580 throw new RuntimeException("-" + BIND_CONF_KEY + " not supported with " + implType); 581 } 582 583 InetSocketAddress inetSocketAddress = new InetSocketAddress(getBindAddress(conf), listenPort); 584 if ( 585 implType == ImplType.HS_HA || implType == ImplType.NONBLOCKING 586 || implType == ImplType.THREADED_SELECTOR 587 ) { 588 TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(inetSocketAddress); 589 if (implType == ImplType.NONBLOCKING) { 590 tserver = getTNonBlockingServer(serverTransport, protocolFactory, processorToUse, 591 transportFactory, inetSocketAddress); 592 } else if (implType == ImplType.HS_HA) { 593 tserver = getTHsHaServer(serverTransport, protocolFactory, processorToUse, transportFactory, 594 inetSocketAddress); 595 } else { // THREADED_SELECTOR 596 tserver = getTThreadedSelectorServer(serverTransport, protocolFactory, processorToUse, 597 transportFactory, inetSocketAddress); 598 } 599 LOG.info("starting HBase {} server on {}", implType.simpleClassName(), 600 Integer.toString(listenPort)); 601 } else if (implType == ImplType.THREAD_POOL) { 602 this.tserver = 603 getTThreadPoolServer(protocolFactory, processorToUse, transportFactory, inetSocketAddress); 604 } else { 605 throw new AssertionError( 606 "Unsupported Thrift server implementation: " + implType.simpleClassName()); 607 } 608 609 // A sanity check that we instantiated the right type of server. 610 if (tserver.getClass() != implType.serverClass) { 611 throw new AssertionError("Expected to create Thrift server class " 612 + implType.serverClass.getName() + " but got " + tserver.getClass().getName()); 613 } 614 } 615 616 protected TServer getTNonBlockingServer(TNonblockingServerTransport serverTransport, 617 TProtocolFactory protocolFactory, TProcessor processor, TTransportFactory transportFactory, 618 InetSocketAddress inetSocketAddress) { 619 LOG.info("starting HBase Nonblocking Thrift server on " + inetSocketAddress.toString()); 620 TNonblockingServer.Args serverArgs = new TNonblockingServer.Args(serverTransport); 621 serverArgs.processor(processor); 622 serverArgs.transportFactory(transportFactory); 623 serverArgs.protocolFactory(protocolFactory); 624 return new TNonblockingServer(serverArgs); 625 } 626 627 protected TServer getTHsHaServer(TNonblockingServerTransport serverTransport, 628 TProtocolFactory protocolFactory, TProcessor processor, TTransportFactory transportFactory, 629 InetSocketAddress inetSocketAddress) { 630 LOG.info("starting HBase HsHA Thrift server on " + inetSocketAddress.toString()); 631 THsHaServer.Args serverArgs = new THsHaServer.Args(serverTransport); 632 int queueSize = conf.getInt(TBoundedThreadPoolServer.MAX_QUEUED_REQUESTS_CONF_KEY, 633 TBoundedThreadPoolServer.DEFAULT_MAX_QUEUED_REQUESTS); 634 CallQueue callQueue = new CallQueue(new LinkedBlockingQueue<>(queueSize), metrics); 635 int workerThread = conf.getInt(TBoundedThreadPoolServer.MAX_WORKER_THREADS_CONF_KEY, 636 serverArgs.getMaxWorkerThreads()); 637 ExecutorService executorService = createExecutor(callQueue, workerThread, workerThread); 638 serverArgs.executorService(executorService).processor(processor) 639 .transportFactory(transportFactory).protocolFactory(protocolFactory); 640 return new THsHaServer(serverArgs); 641 } 642 643 protected TServer getTThreadedSelectorServer(TNonblockingServerTransport serverTransport, 644 TProtocolFactory protocolFactory, TProcessor processor, TTransportFactory transportFactory, 645 InetSocketAddress inetSocketAddress) { 646 LOG.info("starting HBase ThreadedSelector Thrift server on " + inetSocketAddress.toString()); 647 TThreadedSelectorServer.Args serverArgs = 648 new HThreadedSelectorServerArgs(serverTransport, conf); 649 int queueSize = conf.getInt(TBoundedThreadPoolServer.MAX_QUEUED_REQUESTS_CONF_KEY, 650 TBoundedThreadPoolServer.DEFAULT_MAX_QUEUED_REQUESTS); 651 CallQueue callQueue = new CallQueue(new LinkedBlockingQueue<>(queueSize), metrics); 652 int workerThreads = conf.getInt(TBoundedThreadPoolServer.MAX_WORKER_THREADS_CONF_KEY, 653 serverArgs.getWorkerThreads()); 654 int selectorThreads = conf.getInt(THRIFT_SELECTOR_NUM, serverArgs.getSelectorThreads()); 655 serverArgs.selectorThreads(selectorThreads); 656 ExecutorService executorService = createExecutor(callQueue, workerThreads, workerThreads); 657 serverArgs.executorService(executorService).processor(processor) 658 .transportFactory(transportFactory).protocolFactory(protocolFactory); 659 return new TThreadedSelectorServer(serverArgs); 660 } 661 662 protected TServer getTThreadPoolServer(TProtocolFactory protocolFactory, TProcessor processor, 663 TTransportFactory transportFactory, InetSocketAddress inetSocketAddress) throws Exception { 664 LOG.info("starting HBase ThreadPool Thrift server on " + inetSocketAddress.toString()); 665 // Thrift's implementation uses '0' as a placeholder for 'use the default.' 666 int backlog = conf.getInt(BACKLOG_CONF_KEY, BACKLOG_CONF_DEAFULT); 667 int readTimeout = 668 conf.getInt(THRIFT_SERVER_SOCKET_READ_TIMEOUT_KEY, THRIFT_SERVER_SOCKET_READ_TIMEOUT_DEFAULT); 669 TServerTransport serverTransport = 670 new TServerSocket(new TServerSocket.ServerSocketTransportArgs().bindAddr(inetSocketAddress) 671 .backlog(backlog).clientTimeout(readTimeout)); 672 673 TBoundedThreadPoolServer.Args serverArgs = 674 new TBoundedThreadPoolServer.Args(serverTransport, conf); 675 serverArgs.processor(processor).transportFactory(transportFactory) 676 .protocolFactory(protocolFactory); 677 return new TBoundedThreadPoolServer(serverArgs, metrics); 678 } 679 680 protected TProtocolFactory getProtocolFactory() { 681 TProtocolFactory protocolFactory; 682 683 if (conf.getBoolean(COMPACT_CONF_KEY, COMPACT_CONF_DEFAULT)) { 684 LOG.debug("Using compact protocol"); 685 protocolFactory = new TCompactProtocol.Factory(); 686 } else { 687 LOG.debug("Using binary protocol"); 688 protocolFactory = new TBinaryProtocol.Factory(); 689 } 690 691 return protocolFactory; 692 } 693 694 protected ExecutorService createExecutor(BlockingQueue<Runnable> callQueue, int minWorkers, 695 int maxWorkers) { 696 ThreadFactoryBuilder tfb = new ThreadFactoryBuilder(); 697 tfb.setDaemon(true); 698 tfb.setNameFormat("thrift-worker-%d"); 699 ThreadPoolExecutor threadPool = new THBaseThreadPoolExecutor(minWorkers, maxWorkers, 700 Long.MAX_VALUE, TimeUnit.SECONDS, callQueue, tfb.build(), metrics); 701 threadPool.allowCoreThreadTimeOut(true); 702 return threadPool; 703 } 704 705 protected InetAddress getBindAddress(Configuration conf) throws UnknownHostException { 706 String bindAddressStr = conf.get(BIND_CONF_KEY, DEFAULT_BIND_ADDR); 707 return InetAddress.getByName(bindAddressStr); 708 } 709 710 public static void registerFilters(Configuration conf) { 711 String[] filters = conf.getStrings(THRIFT_FILTERS); 712 Splitter splitter = Splitter.on(':'); 713 if (filters != null) { 714 for (String filterClass : filters) { 715 List<String> filterPart = splitter.splitToList(filterClass); 716 if (filterPart.size() != 2) { 717 LOG.warn("Invalid filter specification " + filterClass + " - skipping"); 718 } else { 719 ParseFilter.registerFilter(filterPart.get(0), filterPart.get(1)); 720 } 721 } 722 } 723 } 724 725 /** 726 * Add options to command lines 727 * @param options options 728 */ 729 protected void addOptions(Options options) { 730 options.addOption("b", BIND_OPTION, true, 731 "Address to bind " + "the Thrift server to. [default: " + DEFAULT_BIND_ADDR + "]"); 732 options.addOption("p", PORT_OPTION, true, 733 "Port to bind to [default: " + DEFAULT_LISTEN_PORT + "]"); 734 options.addOption("f", FRAMED_OPTION, false, "Use framed transport"); 735 options.addOption("c", COMPACT_OPTION, false, "Use the compact protocol"); 736 options.addOption("h", "help", false, "Print help information"); 737 options.addOption("s", SELECTOR_NUM_OPTION, true, "How many selector threads to use."); 738 options.addOption(null, INFOPORT_OPTION, true, "Port for web UI"); 739 740 options.addOption("m", MIN_WORKERS_OPTION, true, 741 "The minimum number of worker threads for " + ImplType.THREAD_POOL.simpleClassName()); 742 743 options.addOption("w", MAX_WORKERS_OPTION, true, 744 "The maximum number of worker threads for " + ImplType.THREAD_POOL.simpleClassName()); 745 746 options.addOption("q", MAX_QUEUE_SIZE_OPTION, true, 747 "The maximum number of queued requests in " + ImplType.THREAD_POOL.simpleClassName()); 748 749 options.addOption("k", KEEP_ALIVE_SEC_OPTION, true, 750 "The amount of time in secods to keep a thread alive when idle in " 751 + ImplType.THREAD_POOL.simpleClassName()); 752 753 options.addOption("t", READ_TIMEOUT_OPTION, true, 754 "Amount of time in milliseconds before a server thread will timeout " 755 + "waiting for client to send data on a connected socket. Currently, " 756 + "only applies to TBoundedThreadPoolServer"); 757 758 options.addOptionGroup(ImplType.createOptionGroup()); 759 } 760 761 protected void parseCommandLine(CommandLine cmd, Options options) throws ExitCodeException { 762 // Get port to bind to 763 try { 764 if (cmd.hasOption(PORT_OPTION)) { 765 int listenPort = Integer.parseInt(cmd.getOptionValue(PORT_OPTION)); 766 conf.setInt(PORT_CONF_KEY, listenPort); 767 } 768 } catch (NumberFormatException e) { 769 LOG.error("Could not parse the value provided for the port option", e); 770 printUsageAndExit(options, -1); 771 } 772 // check for user-defined info server port setting, if so override the conf 773 try { 774 if (cmd.hasOption(INFOPORT_OPTION)) { 775 String val = cmd.getOptionValue(INFOPORT_OPTION); 776 conf.setInt(THRIFT_INFO_SERVER_PORT, Integer.parseInt(val)); 777 LOG.debug("Web UI port set to " + val); 778 } 779 } catch (NumberFormatException e) { 780 LOG.error("Could not parse the value provided for the " + INFOPORT_OPTION + " option", e); 781 printUsageAndExit(options, -1); 782 } 783 // Make optional changes to the configuration based on command-line options 784 optionToConf(cmd, MIN_WORKERS_OPTION, conf, 785 TBoundedThreadPoolServer.MIN_WORKER_THREADS_CONF_KEY); 786 optionToConf(cmd, MAX_WORKERS_OPTION, conf, 787 TBoundedThreadPoolServer.MAX_WORKER_THREADS_CONF_KEY); 788 optionToConf(cmd, MAX_QUEUE_SIZE_OPTION, conf, 789 TBoundedThreadPoolServer.MAX_QUEUED_REQUESTS_CONF_KEY); 790 optionToConf(cmd, KEEP_ALIVE_SEC_OPTION, conf, 791 TBoundedThreadPoolServer.THREAD_KEEP_ALIVE_TIME_SEC_CONF_KEY); 792 optionToConf(cmd, READ_TIMEOUT_OPTION, conf, THRIFT_SERVER_SOCKET_READ_TIMEOUT_KEY); 793 optionToConf(cmd, SELECTOR_NUM_OPTION, conf, THRIFT_SELECTOR_NUM); 794 795 // Set general thrift server options 796 boolean compact = cmd.hasOption(COMPACT_OPTION) || conf.getBoolean(COMPACT_CONF_KEY, false); 797 conf.setBoolean(COMPACT_CONF_KEY, compact); 798 boolean framed = cmd.hasOption(FRAMED_OPTION) || conf.getBoolean(FRAMED_CONF_KEY, false); 799 conf.setBoolean(FRAMED_CONF_KEY, framed); 800 801 optionToConf(cmd, BIND_OPTION, conf, BIND_CONF_KEY); 802 803 ImplType.setServerImpl(cmd, conf); 804 } 805 806 /** 807 * Parse the command line options to set parameters the conf. 808 */ 809 protected void processOptions(final String[] args) throws Exception { 810 if (args == null || args.length == 0) { 811 return; 812 } 813 Options options = new Options(); 814 addOptions(options); 815 816 CommandLineParser parser = new DefaultParser(); 817 CommandLine cmd = parser.parse(options, args); 818 819 if (cmd.hasOption("help")) { 820 printUsageAndExit(options, 0); 821 } 822 parseCommandLine(cmd, options); 823 } 824 825 public void stop() { 826 if (this.infoServer != null) { 827 LOG.info("Stopping infoServer"); 828 try { 829 this.infoServer.stop(); 830 } catch (Exception ex) { 831 LOG.error("Failed to stop infoServer", ex); 832 } 833 } 834 if (pauseMonitor != null) { 835 pauseMonitor.stop(); 836 } 837 if (tserver != null) { 838 tserver.stop(); 839 tserver = null; 840 } 841 if (httpServer != null) { 842 try { 843 httpServer.stop(); 844 httpServer = null; 845 } catch (Exception e) { 846 LOG.error("Problem encountered in shutting down HTTP server", e); 847 } 848 httpServer = null; 849 } 850 } 851 852 protected static void optionToConf(CommandLine cmd, String option, Configuration conf, 853 String destConfKey) { 854 if (cmd.hasOption(option)) { 855 String value = cmd.getOptionValue(option); 856 LOG.info("Set configuration key:" + destConfKey + " value:" + value); 857 conf.set(destConfKey, value); 858 } 859 } 860 861 /** 862 * Run without any command line arguments 863 * @return exit code 864 * @throws Exception exception 865 */ 866 public int run() throws Exception { 867 return run(null); 868 } 869 870 @Override 871 public int run(String[] strings) throws Exception { 872 processOptions(strings); 873 setupParamters(); 874 if (httpEnabled) { 875 setupHTTPServer(); 876 } else { 877 setupServer(); 878 } 879 return serviceUGI.doAs(new PrivilegedAction<Integer>() { 880 @Override 881 public Integer run() { 882 try { 883 startInfoServer(); 884 if (httpEnabled) { 885 httpServer.start(); 886 httpServer.join(); 887 } else { 888 tserver.serve(); 889 } 890 return 0; 891 } catch (Exception e) { 892 LOG.error(HBaseMarkers.FATAL, "Cannot run ThriftServer", e); 893 return -1; 894 } 895 } 896 }); 897 } 898 899 public static void main(String[] args) throws Exception { 900 LOG.info("***** STARTING service '" + ThriftServer.class.getSimpleName() + "' *****"); 901 VersionInfo.logVersion(); 902 final Configuration conf = HBaseConfiguration.create(); 903 // for now, only time we return is on an argument error. 904 final int status = ToolRunner.run(conf, new ThriftServer(conf), args); 905 LOG.info("***** STOPPING service '" + ThriftServer.class.getSimpleName() + "' *****"); 906 System.exit(status); 907 } 908}