Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.rest; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.rest.filter.AuthFilter; import org.apache.hadoop.hbase.security.UserProvider; import org.apache.hadoop.hbase.util.HttpServerUtil; import org.apache.hadoop.hbase.util.InfoServer; import org.apache.hadoop.hbase.util.Strings; import org.apache.hadoop.hbase.util.VersionInfo; import org.apache.hadoop.net.DNS; import org.apache.hadoop.security.UserGroupInformation; import org.mortbay.jetty.Connector; import org.mortbay.jetty.Server; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.security.SslSelectChannelConnector; import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.FilterHolder; import org.mortbay.jetty.servlet.ServletHolder; import org.mortbay.thread.QueuedThreadPool; import com.google.common.base.Preconditions; import com.sun.jersey.api.json.JSONConfiguration; import com.sun.jersey.spi.container.servlet.ServletContainer; /** * Main class for launching REST gateway as a servlet hosted by Jetty. * <p> * The following options are supported: * <ul> * <li>-p --port : service port</li> * <li>-ro --readonly : server mode</li> * </ul> */ @InterfaceAudience.Private public class RESTServer implements Constants { private static void printUsageAndExit(Options options, int exitCode) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("bin/hbase rest start", "", options, "\nTo run the REST server as a daemon, execute " + "bin/hbase-daemon.sh start|stop rest [--infoport <port>] [-p <port>] [-ro]\n", true); System.exit(exitCode); } /** * The main method for the HBase rest server. * @param args command-line arguments * @throws Exception exception */ public static void main(String[] args) throws Exception { Log LOG = LogFactory.getLog("RESTServer"); VersionInfo.logVersion(); FilterHolder authFilter = null; Configuration conf = HBaseConfiguration.create(); Class<? extends ServletContainer> containerClass = ServletContainer.class; UserProvider userProvider = UserProvider.instantiate(conf); // login the server principal (if using secure Hadoop) if (userProvider.isHadoopSecurityEnabled() && userProvider.isHBaseSecurityEnabled()) { String machineName = Strings.domainNamePointerToHostName(DNS.getDefaultHost( conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, "default"))); String keytabFilename = conf.get(REST_KEYTAB_FILE); Preconditions.checkArgument(keytabFilename != null && !keytabFilename.isEmpty(), REST_KEYTAB_FILE + " should be set if security is enabled"); String principalConfig = conf.get(REST_KERBEROS_PRINCIPAL); Preconditions.checkArgument(principalConfig != null && !principalConfig.isEmpty(), REST_KERBEROS_PRINCIPAL + " should be set if security is enabled"); userProvider.login(REST_KEYTAB_FILE, REST_KERBEROS_PRINCIPAL, machineName); if (conf.get(REST_AUTHENTICATION_TYPE) != null) { containerClass = RESTServletContainer.class; authFilter = new FilterHolder(); authFilter.setClassName(AuthFilter.class.getName()); authFilter.setName("AuthenticationFilter"); } } UserGroupInformation realUser = userProvider.getCurrent().getUGI(); RESTServlet servlet = RESTServlet.getInstance(conf, realUser); Options options = new Options(); options.addOption("p", "port", true, "Port to bind to [default: 8080]"); options.addOption("ro", "readonly", false, "Respond only to GET HTTP " + "method requests [default: false]"); options.addOption(null, "infoport", true, "Port for web UI"); CommandLine commandLine = null; try { commandLine = new PosixParser().parse(options, args); } catch (ParseException e) { LOG.error("Could not parse: ", e); printUsageAndExit(options, -1); } // check for user-defined port setting, if so override the conf if (commandLine != null && commandLine.hasOption("port")) { String val = commandLine.getOptionValue("port"); servlet.getConfiguration().setInt("hbase.rest.port", Integer.valueOf(val)); LOG.debug("port set to " + val); } // check if server should only process GET requests, if so override the conf if (commandLine != null && commandLine.hasOption("readonly")) { servlet.getConfiguration().setBoolean("hbase.rest.readonly", true); LOG.debug("readonly set to true"); } // check for user-defined info server port setting, if so override the conf if (commandLine != null && commandLine.hasOption("infoport")) { String val = commandLine.getOptionValue("infoport"); servlet.getConfiguration().setInt("hbase.rest.info.port", Integer.valueOf(val)); LOG.debug("Web UI port set to " + val); } @SuppressWarnings("unchecked") List<String> remainingArgs = commandLine != null ? commandLine.getArgList() : new ArrayList<String>(); if (remainingArgs.size() != 1) { printUsageAndExit(options, 1); } String command = remainingArgs.get(0); if ("start".equals(command)) { // continue and start container } else if ("stop".equals(command)) { System.exit(1); } else { printUsageAndExit(options, 1); } // set up the Jersey servlet container for Jetty ServletHolder sh = new ServletHolder(containerClass); sh.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", ResourceConfig.class.getCanonicalName()); sh.setInitParameter("com.sun.jersey.config.property.packages", "jetty"); // The servlet holder below is instantiated to only handle the case // of the /status/cluster returning arrays of nodes (live/dead). Without // this servlet holder, the problem is that the node arrays in the response // are collapsed to single nodes. We want to be able to treat the // node lists as POJO in the response to /status/cluster servlet call, // but not change the behavior for any of the other servlets // Hence we don't use the servlet holder for all servlets / paths ServletHolder shPojoMap = new ServletHolder(containerClass); @SuppressWarnings("unchecked") Map<String, String> shInitMap = sh.getInitParameters(); for (Entry<String, String> e : shInitMap.entrySet()) { shPojoMap.setInitParameter(e.getKey(), e.getValue()); } shPojoMap.setInitParameter(JSONConfiguration.FEATURE_POJO_MAPPING, "true"); // set up Jetty and run the embedded server Server server = new Server(); Connector connector = new SelectChannelConnector(); if (conf.getBoolean(REST_SSL_ENABLED, false)) { SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(); String keystore = conf.get(REST_SSL_KEYSTORE_STORE); String password = conf.get(REST_SSL_KEYSTORE_PASSWORD); String keyPassword = conf.get(REST_SSL_KEYSTORE_KEYPASSWORD, password); sslConnector.setKeystore(keystore); sslConnector.setPassword(password); sslConnector.setKeyPassword(keyPassword); connector = sslConnector; } connector.setPort(servlet.getConfiguration().getInt("hbase.rest.port", 8080)); connector.setHost(servlet.getConfiguration().get("hbase.rest.host", "0.0.0.0")); server.addConnector(connector); // Set the default max thread number to 100 to limit // the number of concurrent requests so that REST server doesn't OOM easily. // Jetty set the default max thread number to 250, if we don't set it. // // Our default min thread number 2 is the same as that used by Jetty. int maxThreads = servlet.getConfiguration().getInt("hbase.rest.threads.max", 100); int minThreads = servlet.getConfiguration().getInt("hbase.rest.threads.min", 2); QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads); threadPool.setMinThreads(minThreads); server.setThreadPool(threadPool); server.setSendServerVersion(false); server.setSendDateHeader(false); server.setStopAtShutdown(true); // set up context Context context = new Context(server, "/", Context.SESSIONS); context.addServlet(shPojoMap, "/status/cluster"); context.addServlet(sh, "/*"); if (authFilter != null) { context.addFilter(authFilter, "/*", 1); } // Load filters from configuration. String[] filterClasses = servlet.getConfiguration().getStrings(FILTER_CLASSES, ArrayUtils.EMPTY_STRING_ARRAY); for (String filter : filterClasses) { filter = filter.trim(); context.addFilter(Class.forName(filter), "/*", 0); } HttpServerUtil.constrainHttpMethods(context); // Put up info server. int port = conf.getInt("hbase.rest.info.port", 8085); if (port >= 0) { conf.setLong("startcode", System.currentTimeMillis()); String a = conf.get("hbase.rest.info.bindAddress", "0.0.0.0"); InfoServer infoServer = new InfoServer("rest", a, port, false, conf); infoServer.setAttribute("hbase.conf", conf); infoServer.start(); } // start server server.start(); server.join(); } }