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