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.rest; 019 020import static org.apache.hadoop.hbase.http.HttpServerUtil.PATH_SPEC_ANY; 021 022import java.lang.management.ManagementFactory; 023import java.net.UnknownHostException; 024import java.util.ArrayList; 025import java.util.EnumSet; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030import java.util.concurrent.ArrayBlockingQueue; 031import javax.servlet.DispatcherType; 032import org.apache.commons.lang3.ArrayUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.HBaseConfiguration; 036import org.apache.hadoop.hbase.HBaseInterfaceAudience; 037import org.apache.hadoop.hbase.ServerName; 038import org.apache.hadoop.hbase.http.HttpServerUtil; 039import org.apache.hadoop.hbase.http.InfoServer; 040import org.apache.hadoop.hbase.log.HBaseMarkers; 041import org.apache.hadoop.hbase.rest.filter.AuthFilter; 042import org.apache.hadoop.hbase.rest.filter.GzipFilter; 043import org.apache.hadoop.hbase.rest.filter.RestCsrfPreventionFilter; 044import org.apache.hadoop.hbase.security.UserProvider; 045import org.apache.hadoop.hbase.util.DNS; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 047import org.apache.hadoop.hbase.util.Pair; 048import org.apache.hadoop.hbase.util.ReflectionUtils; 049import org.apache.hadoop.hbase.util.Strings; 050import org.apache.hadoop.hbase.util.VersionInfo; 051import org.apache.yetus.audience.InterfaceAudience; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 056import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 057import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 058import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter; 059import org.apache.hbase.thirdparty.org.apache.commons.cli.Options; 060import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException; 061import org.apache.hbase.thirdparty.org.apache.commons.cli.PosixParser; 062import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.FilterHolder; 063import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletContextHandler; 064import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletHolder; 065import org.apache.hbase.thirdparty.org.eclipse.jetty.http.HttpVersion; 066import org.apache.hbase.thirdparty.org.eclipse.jetty.http.UriCompliance; 067import org.apache.hbase.thirdparty.org.eclipse.jetty.jmx.MBeanContainer; 068import org.apache.hbase.thirdparty.org.eclipse.jetty.server.HttpConfiguration; 069import org.apache.hbase.thirdparty.org.eclipse.jetty.server.HttpConnectionFactory; 070import org.apache.hbase.thirdparty.org.eclipse.jetty.server.SecureRequestCustomizer; 071import org.apache.hbase.thirdparty.org.eclipse.jetty.server.Server; 072import org.apache.hbase.thirdparty.org.eclipse.jetty.server.ServerConnector; 073import org.apache.hbase.thirdparty.org.eclipse.jetty.server.SslConnectionFactory; 074import org.apache.hbase.thirdparty.org.eclipse.jetty.util.ssl.SslContextFactory; 075import org.apache.hbase.thirdparty.org.eclipse.jetty.util.thread.QueuedThreadPool; 076import org.apache.hbase.thirdparty.org.glassfish.jersey.server.ResourceConfig; 077import org.apache.hbase.thirdparty.org.glassfish.jersey.servlet.ServletContainer; 078 079/** 080 * Main class for launching REST gateway as a servlet hosted by Jetty. 081 * <p> 082 * The following options are supported: 083 * <ul> 084 * <li>-p --port : service port</li> 085 * <li>-ro --readonly : server mode</li> 086 * </ul> 087 */ 088@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 089public class RESTServer implements Constants { 090 static Logger LOG = LoggerFactory.getLogger("RESTServer"); 091 public static final String REST_SERVER = "rest"; 092 093 static final String REST_CSRF_ENABLED_KEY = "hbase.rest.csrf.enabled"; 094 static final boolean REST_CSRF_ENABLED_DEFAULT = false; 095 boolean restCSRFEnabled = false; 096 static final String REST_CSRF_CUSTOM_HEADER_KEY = "hbase.rest.csrf.custom.header"; 097 static final String REST_CSRF_CUSTOM_HEADER_DEFAULT = "X-XSRF-HEADER"; 098 static final String REST_CSRF_METHODS_TO_IGNORE_KEY = "hbase.rest.csrf.methods.to.ignore"; 099 static final String REST_CSRF_METHODS_TO_IGNORE_DEFAULT = "GET,OPTIONS,HEAD,TRACE"; 100 public static final String SKIP_LOGIN_KEY = "hbase.rest.skip.login"; 101 static final int DEFAULT_HTTP_MAX_HEADER_SIZE = 64 * 1024; // 64k 102 static final String HTTP_HEADER_CACHE_SIZE = "hbase.rest.http.header.cache.size"; 103 static final int DEFAULT_HTTP_HEADER_CACHE_SIZE = Character.MAX_VALUE - 1; 104 105 static final String REST_HTTP_ALLOW_OPTIONS_METHOD = "hbase.rest.http.allow.options.method"; 106 // HTTP OPTIONS method is commonly used in REST APIs for negotiation. So it is enabled by default. 107 private static boolean REST_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT = true; 108 static final String REST_CSRF_BROWSER_USERAGENTS_REGEX_KEY = 109 "hbase.rest-csrf.browser-useragents-regex"; 110 111 // HACK, making this static for AuthFilter to get at our configuration. Necessary for unit tests. 112 @edu.umd.cs.findbugs.annotations.SuppressWarnings( 113 value = { "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", "MS_CANNOT_BE_FINAL" }, 114 justification = "For testing") 115 public static Configuration conf = null; 116 private final UserProvider userProvider; 117 private Server server; 118 private InfoServer infoServer; 119 private ServerName serverName; 120 121 public RESTServer(Configuration conf) { 122 RESTServer.conf = conf; 123 this.userProvider = UserProvider.instantiate(conf); 124 } 125 126 private static void printUsageAndExit(Options options, int exitCode) { 127 HelpFormatter formatter = new HelpFormatter(); 128 formatter.printHelp("hbase rest start", "", options, 129 "\nTo run the REST server as a daemon, execute " 130 + "hbase-daemon.sh start|stop rest [-i <port>] [-p <port>] [-ro]\n", 131 true); 132 System.exit(exitCode); 133 } 134 135 void addCSRFFilter(ServletContextHandler ctxHandler, Configuration conf) { 136 restCSRFEnabled = conf.getBoolean(REST_CSRF_ENABLED_KEY, REST_CSRF_ENABLED_DEFAULT); 137 if (restCSRFEnabled) { 138 Map<String, String> restCsrfParams = 139 RestCsrfPreventionFilter.getFilterParams(conf, "hbase.rest-csrf."); 140 FilterHolder holder = new FilterHolder(); 141 holder.setName("csrf"); 142 holder.setClassName(RestCsrfPreventionFilter.class.getName()); 143 holder.setInitParameters(restCsrfParams); 144 ctxHandler.addFilter(holder, PATH_SPEC_ANY, EnumSet.allOf(DispatcherType.class)); 145 } 146 } 147 148 // login the server principal (if using secure Hadoop) 149 private static Pair<FilterHolder, Class<? extends ServletContainer>> 150 loginServerPrincipal(UserProvider userProvider, Configuration conf) throws Exception { 151 Class<? extends ServletContainer> containerClass = ServletContainer.class; 152 if (userProvider.isHadoopSecurityEnabled() && userProvider.isHBaseSecurityEnabled()) { 153 String machineName = getHostName(conf); 154 String keytabFilename = conf.get(REST_KEYTAB_FILE); 155 Preconditions.checkArgument(keytabFilename != null && !keytabFilename.isEmpty(), 156 REST_KEYTAB_FILE + " should be set if security is enabled"); 157 String principalConfig = conf.get(REST_KERBEROS_PRINCIPAL); 158 Preconditions.checkArgument(principalConfig != null && !principalConfig.isEmpty(), 159 REST_KERBEROS_PRINCIPAL + " should be set if security is enabled"); 160 // Hook for unit tests, this will log out any other user and mess up tests. 161 if (!conf.getBoolean(SKIP_LOGIN_KEY, false)) { 162 userProvider.login(REST_KEYTAB_FILE, REST_KERBEROS_PRINCIPAL, machineName); 163 } 164 if (conf.get(REST_AUTHENTICATION_TYPE) != null) { 165 containerClass = RESTServletContainer.class; 166 FilterHolder authFilter = new FilterHolder(); 167 authFilter.setClassName(AuthFilter.class.getName()); 168 authFilter.setName("AuthenticationFilter"); 169 return new Pair<>(authFilter, containerClass); 170 } 171 } 172 return new Pair<>(null, containerClass); 173 } 174 175 private static void parseCommandLine(String[] args, Configuration conf) { 176 Options options = new Options(); 177 options.addOption("p", "port", true, "Port to bind to [default: " + DEFAULT_LISTEN_PORT + "]"); 178 options.addOption("ro", "readonly", false, 179 "Respond only to GET HTTP " + "method requests [default: false]"); 180 options.addOption("i", "infoport", true, "Port for WEB UI"); 181 182 CommandLine commandLine = null; 183 try { 184 commandLine = new PosixParser().parse(options, args); 185 } catch (ParseException e) { 186 LOG.error("Could not parse: ", e); 187 printUsageAndExit(options, -1); 188 } 189 190 // check for user-defined port setting, if so override the conf 191 if (commandLine != null && commandLine.hasOption("port")) { 192 String val = commandLine.getOptionValue("port"); 193 conf.setInt("hbase.rest.port", Integer.parseInt(val)); 194 if (LOG.isDebugEnabled()) { 195 LOG.debug("port set to " + val); 196 } 197 } 198 199 // check if server should only process GET requests, if so override the conf 200 if (commandLine != null && commandLine.hasOption("readonly")) { 201 conf.setBoolean("hbase.rest.readonly", true); 202 if (LOG.isDebugEnabled()) { 203 LOG.debug("readonly set to true"); 204 } 205 } 206 207 // check for user-defined info server port setting, if so override the conf 208 if (commandLine != null && commandLine.hasOption("infoport")) { 209 String val = commandLine.getOptionValue("infoport"); 210 conf.setInt("hbase.rest.info.port", Integer.parseInt(val)); 211 if (LOG.isDebugEnabled()) { 212 LOG.debug("WEB UI port set to " + val); 213 } 214 } 215 216 if (commandLine != null && commandLine.hasOption("skipLogin")) { 217 conf.setBoolean(SKIP_LOGIN_KEY, true); 218 if (LOG.isDebugEnabled()) { 219 LOG.debug("Skipping Kerberos login for REST server"); 220 } 221 } 222 223 List<String> remainingArgs = commandLine != null ? commandLine.getArgList() : new ArrayList<>(); 224 if (remainingArgs.size() != 1) { 225 printUsageAndExit(options, 1); 226 } 227 228 String command = remainingArgs.get(0); 229 if ("start".equals(command)) { 230 // continue and start container 231 } else if ("stop".equals(command)) { 232 System.exit(1); 233 } else { 234 printUsageAndExit(options, 1); 235 } 236 } 237 238 /** 239 * Runs the REST server. 240 */ 241 public synchronized void run() throws Exception { 242 Pair<FilterHolder, Class<? extends ServletContainer>> pair = 243 loginServerPrincipal(userProvider, conf); 244 FilterHolder authFilter = pair.getFirst(); 245 Class<? extends ServletContainer> containerClass = pair.getSecond(); 246 RESTServlet servlet = RESTServlet.getInstance(conf, userProvider); 247 248 // set up the Jersey servlet container for Jetty 249 ResourceConfig application = new ResourceConfig().packages("org.apache.hadoop.hbase.rest") 250 .register(JacksonJaxbJsonProvider.class); 251 // Using our custom ServletContainer is tremendously important. This is what makes sure the 252 // UGI.doAs() is done for the remoteUser, and calls are not made as the REST server itself. 253 ServletContainer servletContainer = ReflectionUtils.newInstance(containerClass, application); 254 ServletHolder sh = new ServletHolder(servletContainer); 255 256 // Set the default max thread number to 100 to limit 257 // the number of concurrent requests so that REST server doesn't OOM easily. 258 // Jetty set the default max thread number to 250, if we don't set it. 259 // 260 // Our default min thread number 2 is the same as that used by Jetty. 261 int maxThreads = servlet.getConfiguration().getInt(REST_THREAD_POOL_THREADS_MAX, 100); 262 int minThreads = servlet.getConfiguration().getInt(REST_THREAD_POOL_THREADS_MIN, 2); 263 // Use the default queue (unbounded with Jetty 9.3) if the queue size is negative, otherwise use 264 // bounded {@link ArrayBlockingQueue} with the given size 265 int queueSize = servlet.getConfiguration().getInt(REST_THREAD_POOL_TASK_QUEUE_SIZE, -1); 266 int idleTimeout = 267 servlet.getConfiguration().getInt(REST_THREAD_POOL_THREAD_IDLE_TIMEOUT, 60000); 268 QueuedThreadPool threadPool = queueSize > 0 269 ? new QueuedThreadPool(maxThreads, minThreads, idleTimeout, 270 new ArrayBlockingQueue<>(queueSize)) 271 : new QueuedThreadPool(maxThreads, minThreads, idleTimeout); 272 273 this.server = new Server(threadPool); 274 275 // Setup JMX 276 MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); 277 server.addEventListener(mbContainer); 278 server.addBean(mbContainer); 279 280 String host = servlet.getConfiguration().get("hbase.rest.host", "0.0.0.0"); 281 int servicePort = servlet.getConfiguration().getInt("hbase.rest.port", 8080); 282 int httpHeaderCacheSize = 283 servlet.getConfiguration().getInt(HTTP_HEADER_CACHE_SIZE, DEFAULT_HTTP_HEADER_CACHE_SIZE); 284 HttpConfiguration httpConfig = new HttpConfiguration(); 285 httpConfig.setSecureScheme("https"); 286 httpConfig.setSecurePort(servicePort); 287 httpConfig.setHeaderCacheSize(httpHeaderCacheSize); 288 httpConfig.setRequestHeaderSize(DEFAULT_HTTP_MAX_HEADER_SIZE); 289 httpConfig.setResponseHeaderSize(DEFAULT_HTTP_MAX_HEADER_SIZE); 290 httpConfig.setSendServerVersion(false); 291 httpConfig.setSendDateHeader(false); 292 293 // In Jetty 12, ambiguous path separators, suspicious path characters, and ambiguous empty 294 // segments are considered violations of the URI specification and hence are not allowed. 295 // Refer to https://github.com/jetty/jetty.project/issues/11890#issuecomment-2156449534 296 // We must set a URI compliance to allow for this violation so that client requests are not 297 // automatically rejected. Our rest endpoints rely on this behavior to handle encoded uri paths. 298 setUriComplianceRules(httpConfig); 299 300 ServerConnector serverConnector; 301 boolean isSecure = false; 302 if (conf.getBoolean(REST_SSL_ENABLED, false)) { 303 isSecure = true; 304 HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); 305 httpsConfig.addCustomizer(new SecureRequestCustomizer()); 306 307 SslContextFactory.Server sslCtxFactory = new SslContextFactory.Server(); 308 String keystore = conf.get(REST_SSL_KEYSTORE_STORE); 309 String keystoreType = conf.get(REST_SSL_KEYSTORE_TYPE); 310 String password = HBaseConfiguration.getPassword(conf, REST_SSL_KEYSTORE_PASSWORD, null); 311 String keyPassword = 312 HBaseConfiguration.getPassword(conf, REST_SSL_KEYSTORE_KEYPASSWORD, password); 313 sslCtxFactory.setKeyStorePath(keystore); 314 if (StringUtils.isNotBlank(keystoreType)) { 315 sslCtxFactory.setKeyStoreType(keystoreType); 316 } 317 sslCtxFactory.setKeyStorePassword(password); 318 sslCtxFactory.setKeyManagerPassword(keyPassword); 319 320 String trustStore = conf.get(REST_SSL_TRUSTSTORE_STORE); 321 if (StringUtils.isNotBlank(trustStore)) { 322 sslCtxFactory.setTrustStorePath(trustStore); 323 } 324 String trustStorePassword = 325 HBaseConfiguration.getPassword(conf, REST_SSL_TRUSTSTORE_PASSWORD, null); 326 if (StringUtils.isNotBlank(trustStorePassword)) { 327 sslCtxFactory.setTrustStorePassword(trustStorePassword); 328 } 329 String trustStoreType = conf.get(REST_SSL_TRUSTSTORE_TYPE); 330 if (StringUtils.isNotBlank(trustStoreType)) { 331 sslCtxFactory.setTrustStoreType(trustStoreType); 332 } 333 334 String[] excludeCiphers = servlet.getConfiguration() 335 .getStrings(REST_SSL_EXCLUDE_CIPHER_SUITES, ArrayUtils.EMPTY_STRING_ARRAY); 336 if (excludeCiphers.length != 0) { 337 sslCtxFactory.setExcludeCipherSuites(excludeCiphers); 338 } 339 String[] includeCiphers = servlet.getConfiguration() 340 .getStrings(REST_SSL_INCLUDE_CIPHER_SUITES, ArrayUtils.EMPTY_STRING_ARRAY); 341 if (includeCiphers.length != 0) { 342 sslCtxFactory.setIncludeCipherSuites(includeCiphers); 343 } 344 345 String[] excludeProtocols = servlet.getConfiguration().getStrings(REST_SSL_EXCLUDE_PROTOCOLS, 346 ArrayUtils.EMPTY_STRING_ARRAY); 347 if (excludeProtocols.length != 0) { 348 sslCtxFactory.setExcludeProtocols(excludeProtocols); 349 } 350 String[] includeProtocols = servlet.getConfiguration().getStrings(REST_SSL_INCLUDE_PROTOCOLS, 351 ArrayUtils.EMPTY_STRING_ARRAY); 352 if (includeProtocols.length != 0) { 353 sslCtxFactory.setIncludeProtocols(includeProtocols); 354 } 355 356 serverConnector = new ServerConnector(server, 357 new SslConnectionFactory(sslCtxFactory, HttpVersion.HTTP_1_1.toString()), 358 new HttpConnectionFactory(httpsConfig)); 359 } else { 360 serverConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); 361 } 362 363 int acceptQueueSize = servlet.getConfiguration().getInt(REST_CONNECTOR_ACCEPT_QUEUE_SIZE, -1); 364 if (acceptQueueSize >= 0) { 365 serverConnector.setAcceptQueueSize(acceptQueueSize); 366 } 367 368 serverConnector.setPort(servicePort); 369 serverConnector.setHost(host); 370 371 server.addConnector(serverConnector); 372 server.setStopAtShutdown(true); 373 374 // set up context 375 ServletContextHandler ctxHandler = 376 new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); 377 ctxHandler.addServlet(sh, PATH_SPEC_ANY); 378 if (authFilter != null) { 379 ctxHandler.addFilter(authFilter, PATH_SPEC_ANY, EnumSet.of(DispatcherType.REQUEST)); 380 } 381 382 // Load filters from configuration. 383 String[] filterClasses = 384 servlet.getConfiguration().getStrings(FILTER_CLASSES, GzipFilter.class.getName()); 385 for (String filter : filterClasses) { 386 filter = filter.trim(); 387 ctxHandler.addFilter(filter, PATH_SPEC_ANY, EnumSet.of(DispatcherType.REQUEST)); 388 } 389 addCSRFFilter(ctxHandler, conf); 390 HttpServerUtil.addClickjackingPreventionFilter(ctxHandler, conf, PATH_SPEC_ANY); 391 HttpServerUtil.addSecurityHeadersFilter(ctxHandler, conf, isSecure, PATH_SPEC_ANY); 392 HttpServerUtil.constrainHttpMethods(ctxHandler, servlet.getConfiguration() 393 .getBoolean(REST_HTTP_ALLOW_OPTIONS_METHOD, REST_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT)); 394 395 // Put up info server. 396 int port = conf.getInt("hbase.rest.info.port", 8085); 397 if (port >= 0) { 398 final long startCode = EnvironmentEdgeManager.currentTime(); 399 conf.setLong("startcode", startCode); 400 this.serverName = ServerName.valueOf(getHostName(conf), servicePort, startCode); 401 402 String addr = conf.get("hbase.rest.info.bindAddress", "0.0.0.0"); 403 this.infoServer = new InfoServer(REST_SERVER, addr, port, false, conf); 404 this.infoServer.addPrivilegedServlet("dump", "/dump", RESTDumpServlet.class); 405 this.infoServer.setAttribute(REST_SERVER, this); 406 this.infoServer.setAttribute("hbase.conf", conf); 407 this.infoServer.start(); 408 } 409 // start server 410 server.start(); 411 } 412 413 private static void setUriComplianceRules(HttpConfiguration httpConfig) { 414 Set<UriCompliance.Violation> complianceViolationSet = new HashSet<>(); 415 complianceViolationSet.add(UriCompliance.Violation.AMBIGUOUS_PATH_SEPARATOR); 416 complianceViolationSet.add(UriCompliance.Violation.SUSPICIOUS_PATH_CHARACTERS); 417 complianceViolationSet.add(UriCompliance.Violation.AMBIGUOUS_EMPTY_SEGMENT); 418 httpConfig.setUriCompliance(UriCompliance.from(complianceViolationSet)); 419 } 420 421 private static String getHostName(Configuration conf) throws UnknownHostException { 422 return Strings.domainNamePointerToHostName(DNS.getDefaultHost( 423 conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, "default"))); 424 } 425 426 public synchronized void join() throws Exception { 427 if (server == null) { 428 throw new IllegalStateException("Server is not running"); 429 } 430 server.join(); 431 } 432 433 private void stopInfoServer() { 434 if (this.infoServer != null) { 435 LOG.info("Stop info server"); 436 try { 437 this.infoServer.stop(); 438 } catch (Exception e) { 439 LOG.error("Failed to stop infoServer", e); 440 } 441 } 442 } 443 444 public synchronized void stop() throws Exception { 445 stopInfoServer(); 446 if (server == null) { 447 throw new IllegalStateException("Server is not running"); 448 } 449 server.stop(); 450 server = null; 451 RESTServlet.stop(); 452 } 453 454 public synchronized int getPort() { 455 if (server == null) { 456 throw new IllegalStateException("Server is not running"); 457 } 458 return ((ServerConnector) server.getConnectors()[0]).getLocalPort(); 459 } 460 461 @SuppressWarnings("deprecation") 462 public synchronized int getInfoPort() { 463 if (infoServer == null) { 464 throw new IllegalStateException("InfoServer is not running"); 465 } 466 return infoServer.getPort(); 467 } 468 469 public ServerName getServerName() { 470 return serverName; 471 } 472 473 public Configuration getConf() { 474 return conf; 475 } 476 477 /** 478 * The main method for the HBase rest server. 479 * @param args command-line arguments 480 * @throws Exception exception 481 */ 482 public static void main(String[] args) throws Exception { 483 LOG.info("***** STARTING service '" + RESTServer.class.getSimpleName() + "' *****"); 484 VersionInfo.logVersion(); 485 final Configuration conf = HBaseConfiguration.create(); 486 parseCommandLine(args, conf); 487 RESTServer server = new RESTServer(conf); 488 489 try { 490 server.run(); 491 server.join(); 492 } catch (Exception e) { 493 LOG.error(HBaseMarkers.FATAL, "Failed to start server", e); 494 System.exit(1); 495 } 496 497 LOG.info("***** STOPPING service '" + RESTServer.class.getSimpleName() + "' *****"); 498 } 499}