| # |
| # |
| # 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. |
| # |
| # File passed to org.jruby.Main by bin/hbase. Pollutes jirb with hbase imports |
| # and hbase commands and then loads jirb. Outputs a banner that tells user |
| # where to find help, shell version, and loads up a custom hirb. |
| # |
| # In noninteractive mode, runs commands from stdin until completion or an error. |
| # On success will exit with status 0, on any problem will exit non-zero. Callers |
| # should only rely on "not equal to 0", because the current error exit code of 1 |
| # will likely be updated to diffentiate e.g. invalid commands, incorrect args, |
| # permissions, etc. |
| |
| # TODO: Interrupt a table creation or a connection to a bad master. Currently |
| # has to time out. Below we've set down the retries for rpc and hbase but |
| # still can be annoying (And there seem to be times when we'll retry for |
| # ever regardless) |
| # TODO: Add support for listing and manipulating catalog tables, etc. |
| # TODO: Encoding; need to know how to go from ruby String to UTF-8 bytes |
| |
| # Run the java magic include and import basic HBase types that will help ease |
| # hbase hacking. |
| include Java |
| |
| # Some goodies for hirb. Should these be left up to the user's discretion? |
| require 'irb/completion' |
| require 'pathname' |
| |
| # Add the directory names in hbase.jruby.sources commandline option |
| # to the ruby load path so I can load up my HBase ruby modules |
| sources = java.lang.System.getProperty('hbase.ruby.sources') |
| $LOAD_PATH.unshift Pathname.new(sources) |
| |
| # |
| # FIXME: Switch args processing to getopt |
| # |
| # See if there are args for this shell. If any, read and then strip from ARGV |
| # so they don't go through to irb. Output shell 'usage' if user types '--help' |
| cmdline_help = <<HERE # HERE document output as shell usage |
| Usage: shell [OPTIONS] [SCRIPTFILE [ARGUMENTS]] |
| |
| -d | --debug Set DEBUG log levels. |
| -h | --help This help. |
| -n | --noninteractive Do not run within an IRB session |
| and exit with non-zero status on |
| first error. |
| HERE |
| found = [] |
| script2run = nil |
| log_level = org.apache.log4j.Level::ERROR |
| @shell_debug = false |
| interactive = true |
| for arg in ARGV |
| if arg == '-h' || arg == '--help' |
| puts cmdline_help |
| exit |
| elsif arg == '-d' || arg == '--debug' |
| log_level = org.apache.log4j.Level::DEBUG |
| $fullBackTrace = true |
| @shell_debug = true |
| found.push(arg) |
| puts "Setting DEBUG log level..." |
| elsif arg == '-n' || arg == '--noninteractive' |
| interactive = false |
| found.push(arg) |
| elsif arg == '-r' || arg == '--return-values' |
| warn '[INFO] the -r | --return-values option is ignored. we always behave '\ |
| 'as though it was given.' |
| found.push(arg) |
| else |
| # Presume it a script. Save it off for running later below |
| # after we've set up some environment. |
| script2run = arg |
| found.push(arg) |
| # Presume that any other args are meant for the script. |
| break |
| end |
| end |
| |
| # Delete all processed args |
| found.each { |arg| ARGV.delete(arg) } |
| # Make sure debug flag gets back to IRB |
| if @shell_debug |
| ARGV.unshift('-d') |
| end |
| |
| # Set logging level to avoid verboseness |
| org.apache.log4j.Logger.getLogger("org.apache.zookeeper").setLevel(log_level) |
| org.apache.log4j.Logger.getLogger("org.apache.hadoop.hbase").setLevel(log_level) |
| |
| # Require HBase now after setting log levels |
| require 'hbase' |
| |
| # Load hbase shell |
| require 'shell' |
| |
| # Require formatter |
| require 'shell/formatter' |
| |
| # Setup the HBase module. Create a configuration. |
| @hbase = Hbase::Hbase.new |
| |
| # Setup console |
| @shell = Shell::Shell.new(@hbase, interactive) |
| @shell.debug = @shell_debug |
| |
| # Add commands to this namespace |
| # TODO avoid polluting main namespace by using a binding |
| @shell.export_commands(self) |
| |
| # Add help command |
| def help(command = nil) |
| @shell.help(command) |
| end |
| |
| # Backwards compatibility method |
| def tools |
| @shell.help_group('tools') |
| end |
| |
| # Debugging method |
| def debug |
| if @shell_debug |
| @shell_debug = false |
| conf.back_trace_limit = 0 |
| log_level = org.apache.log4j.Level::ERROR |
| else |
| @shell_debug = true |
| conf.back_trace_limit = 100 |
| log_level = org.apache.log4j.Level::DEBUG |
| end |
| org.apache.log4j.Logger.getLogger("org.apache.zookeeper").setLevel(log_level) |
| org.apache.log4j.Logger.getLogger("org.apache.hadoop.hbase").setLevel(log_level) |
| debug? |
| end |
| |
| def debug? |
| puts "Debug mode is #{@shell_debug ? 'ON' : 'OFF'}\n\n" |
| nil |
| end |
| |
| # Include hbase constants |
| include HBaseConstants |
| |
| # If script2run, try running it. If we're in interactive mode, will go on to run the shell unless |
| # script calls 'exit' or 'exit 0' or 'exit errcode'. |
| load(script2run) if script2run |
| |
| if interactive |
| # Output a banner message that tells users where to go for help |
| @shell.print_banner |
| |
| require "irb" |
| require 'irb/hirb' |
| |
| module IRB |
| def self.start(ap_path = nil) |
| $0 = File::basename(ap_path, ".rb") if ap_path |
| |
| IRB.setup(ap_path) |
| @CONF[:IRB_NAME] = 'hbase' |
| @CONF[:AP_NAME] = 'hbase' |
| @CONF[:BACK_TRACE_LIMIT] = 0 unless $fullBackTrace |
| |
| if @CONF[:SCRIPT] |
| hirb = HIRB.new(nil, @CONF[:SCRIPT]) |
| else |
| hirb = HIRB.new |
| end |
| |
| @CONF[:IRB_RC].call(hirb.context) if @CONF[:IRB_RC] |
| @CONF[:MAIN_CONTEXT] = hirb.context |
| |
| catch(:IRB_EXIT) do |
| hirb.eval_input |
| end |
| end |
| end |
| |
| IRB.start |
| else |
| begin |
| # Noninteractive mode: if there is input on stdin, do a simple REPL. |
| # XXX Note that this purposefully uses STDIN and not Kernel.gets |
| # in order to maintain compatibility with previous behavior where |
| # a user could pass in script2run and then still pipe commands on |
| # stdin. |
| require "irb/ruby-lex" |
| require "irb/workspace" |
| workspace = IRB::WorkSpace.new(binding()) |
| scanner = RubyLex.new |
| scanner.set_input(STDIN) |
| scanner.each_top_level_statement do |statement, linenum| |
| puts(workspace.evaluate(nil, statement, 'stdin', linenum)) |
| end |
| # XXX We're catching Exception on purpose, because we want to include |
| # unwrapped java exceptions, syntax errors, eval failures, etc. |
| rescue Exception => exception |
| message = exception.to_s |
| # exception unwrapping in shell means we'll have to handle Java exceptions |
| # as a special case in order to format them properly. |
| if exception.kind_of? java.lang.Exception |
| $stderr.puts "java exception" |
| message = exception.get_message |
| end |
| # Include the 'ERROR' string to try to make transition easier for scripts that |
| # may have already been relying on grepping output. |
| puts "ERROR #{exception.class}: #{message}" |
| if $fullBacktrace |
| # re-raising the will include a backtrace and exit. |
| raise exception |
| else |
| exit 1 |
| end |
| end |
| end |