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