#!/usr/bin/perl package JMX::Jmx4Perl::J4psh::CommandHandler; use strict; use Data::Dumper; use Term::ANSIColor qw(:constants); use Module::Find; =head1 NAME JMX::Jmx4Perl::J4psh::CommandHandler - Handler for j4psh commands =head1 DESCRIPTION This object is responsible for managing L objects which are at the heart of j4psh and provide all features. During startup it registeres commands dynamically and pushes the L context to them for allowing to access the agent and other handlers. Registration is occurs in two phases: ... It also keeps a stack of so called navigational I which can be used to provide a menu like structure (think of it like directories which can be entered). If the stack contains elements, the navigational commands C<..> and C are added to traverse the stack. C will always jump to the top of the stack (the I) whereas C<..> will pop up one level in the stack (the I). Commands which want to manipulate the stack like pushing themselves on the stack should use the methods L or L (for jumping to the top of the menu). =cut =head1 METHODS =over =item $command_handler = new JMX::Jmx4Perl::Shell::CommandHandler($context,$ui) Create a new command handler object. The arguments to be passed are the context object (C<$context>) and the shell object (C<$shell>) in order to update the shell's current command set. =cut sub new { my $class = shift; my $context = shift || "No context object given"; my $shell = shift || "No shell given"; my $extra = shift; $extra = { $extra, @_ } unless ref($extra) eq "HASH"; my $self = { context => $context, shell => $shell, %{$extra} }; $self->{stack} = []; bless $self,(ref($class) || $class); $shell->term->prompt($self->_prompt); $self->_register_commands; return $self; } =item $comand_handler->push_on_stack($context,$cmds) Update the stack with an entry of name C<$context> which provides the commands C<$cmds>. C<$cmds> must be a hashref as known to L, whose C method is used to update the shell. Additionally it updates the shell's prompt to reflect the state of the stack. =cut sub push_on_stack { my $self = shift; # The new context my $context = shift; # Sub-commands within the context my $sub_cmds = shift; my $separator = shift || "/"; my $contexts = $self->{stack}; push @$contexts,{ name => $context, cmds => $sub_cmds, separator => $separator }; #print Dumper(\@contexts); my $shell = $self->{shell}; # Set sub-commands $shell->commands ({ %$sub_cmds, %{$self->_global_commands}, %{$self->_navigation_commands}, } ); } =item $command_handler->reset_stack Reset the stack and install the top and global commands as collected from the registered L. =cut sub reset_stack { my $self = shift; my $shell = $self->{shell}; $shell->commands({ %{$self->_top_commands}, %{$self->_global_commands}}); $self->{stack} = []; } =item $command = $command_handler->command($command_name) Get a registered command by name =cut sub command { my $self = shift; my $name = shift || die "No command name given"; return $self->{commands}->{$name}; } =back =cut # ============================================================================ sub _top_commands { my $self = shift; my $top = $self->{top_commands}; my @ret = (); for my $command (values %$top) { push @ret, %{$command->top_commands}; } return { @ret }; } sub _global_commands { my $self = shift; my $globals = $self->{global_commands}; my @ret = (); for my $command (values %$globals) { push @ret, %{$command->global_commands}; } return { @ret }; } sub _navigation_commands { my $self = shift; my $shell = $self->{shell}; my $contexts = $self->{stack}; if (@$contexts > 0) { return {".." => { desc => "Go up one level", proc => sub { $self->pop_off_stack(); } }, "/" => { desc => "Go to the top level", proc => sub { $self->reset_stack(); } } }; } else { return {}; } } # Go up one in the hierarchy sub pop_off_stack { my $self = shift; my $shell = $self->{shell}; my $stack = $self->{stack}; my $parent = pop @$stack; if (@$stack > 0) { $shell->commands ({ %{$stack->[$#{$stack}]->{cmds}}, %{$self->_global_commands}, %{$self->_navigation_commands}, } ); } else { $shell->commands({ %{$self->_top_commands}, %{$self->_global_commands}, }); } } sub _register_commands { my $self = shift; my $context = $self->{context}; my $modules = $self->find_commands(); my $commands = {}; my $top = {}; my $globals = {}; for my $module (@$modules) { my $file = $module; $file =~ s/::/\//g; require $file . ".pm"; $module->import; my $command = eval "$module->new(\$context)"; die "Cannot register $module: ",$@ if $@; $commands->{$command->name} = $command; my $top_cmd = $command->top_commands; if ($top_cmd) { $top->{$command->name} = $command; } my $global_cmd = $command->global_commands; if ($global_cmd) { $globals->{$command->name} = $command; } } $self->{commands} = $commands; $self->{top_commands} = $top; $self->{global_commands} = $globals; $self->reset_stack; } sub find_commands { my $self = shift; my $command_pkgs = ref($self->{command_packages}) eq "ARRAY" ? $self->{command_packages} : [ $self->{command_packages} ]; my @modules = (); for my $pkg (@{$command_pkgs}) { for my $command (findsubmod $pkg) { next unless $command; push @modules,$command; } } if ($self->{command_modules}) { my $command_modules = ref($self->{command_modules}) eq "ARRAY" ? $self->{command_modules} : [ $self->{command_modules} ]; for my $command (@$command_modules) { push @modules,$command; } } return \@modules; } sub _prompt { my $self = shift; my $context = $self->{context}; my $shell = $self->{shell}; return sub { my $term = shift; my $stack = $self->{stack}; my $agent = $context->agent; my ($c_host,$c_context,$c_empty,$reset) = $self->{no_color_prompt} ? ("","","","") : $shell->color("host","prompt_context","prompt_empty",RESET,{escape => 1}); my $p = "["; $p .= $agent ? $c_host . $context->server : $c_empty . $context->name; $p .= $reset; $p .= " " . $c_context if @$stack; for my $i (0 .. $#{$stack}) { $p .= $stack->[$i]->{name}; $p .= $i < $#{$stack} ? $stack->[$i]->{separator} : $reset; } $p .= "] : "; return $p; }; } =head1 LICENSE This file is part of jmx4perl. Jmx4perl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. jmx4perl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with jmx4perl. If not, see . A commercial license is available as well. Please contact roland@cpan.org for further details. =head1 PROFESSIONAL SERVICES Just in case you need professional support for this module (or Nagios or JMX in general), you might want to have a look at http://www.consol.com/opensource/nagios/. Contact roland.huss@consol.de for further information (or use the contact form at http://www.consol.com/contact/) =head1 AUTHOR roland@cpan.org =cut 1;