File Coverage

lib/App.pm
Criterion Covered Total %
statement 86 254 33.8
branch 42 190 22.1
condition 0 36 0.0
subroutine 15 22 68.1
pod 10 12 83.3
total 153 514 29.7


line stmt bran cond sub pod time code
1              
2             #############################################################################
3             ## $Id: App.pm 13887 2010-04-06 13:36:42Z spadkins $
4             #############################################################################
5              
6             package App;
7             $VERSION = (q$Revision: 13887 $ =~ /(\d[\d\.]*)/)[0]; # VERSION numbers generated by svn
8              
9 9     9   210265 use strict;
  9         21  
  9         2308  
10              
11             # eliminate warnings about uninitialized values
12             $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /Use of uninitialized value/};
13              
14 9     9   7718 use Exception::Class; # enable Exception inheritance
  8         150785  
  8         58  
15 8     8   4423 use App::Exceptions;
  8         29  
  8         239  
16 8     8   8864 use IO::Handle;
  8         68831  
  8         5887  
17              
18             =head1 NAME
19              
20             App - Backplane for core App services
21              
22             =head1 SYNOPSIS
23              
24             use App;
25              
26             my ($context, $conf, $object);
27             $context = App->context(); # singleton per process
28             $conf = App->conf(); # returns a hashref to conf
29             $context = App->new();
30             $object = App->new($class);
31             $object = App->new($class, $method);
32             $object = App->new($class, $method, @args);
33              
34             =head1 DOCUMENT STATUS
35              
36             This documentation is out of date and needs review and revision.
37              
38             Please start with the L document.
39              
40             =head1 DESCRIPTION
41              
42             The App module is the module from which core services are called.
43              
44             =cut
45              
46             #############################################################################
47             # DISTRIBUTION
48             #############################################################################
49              
50             =head1 Distribution: App-Context
51              
52             The App-Context distribution is the core set of modules implementing
53             the core of an enterprise application development framework.
54              
55             http://www.officevision.com/pub/App-Context
56              
57             * Version: 0.01
58              
59             It provides the following services.
60              
61             * Application Configuration (App::Conf::*)
62             * Session Management (App::Session::*)
63             * Remote Procedure Call (App::Procedure::*)
64             * Session Objects and Remote Method Invocation (App::SessionObject::*)
65             * Multiprocess-safe Name-Value Storage (App::SharedDatastore::*)
66             * Shared Resource Pooling and Locking (App::SharedResourceSet::*)
67              
68             One of App-Context's extended services (App::Repository::*)
69             adds distributed transaction capabilities and access to data
70             from a variety of sources through a uniform interface.
71              
72             In the same distribution (App-Repository), is a base class,
73             App::RepositoryObject, which serves as the base class for
74             implementing persistent business objects.
75              
76             http://www.officevision.com/pub/App-Repository
77              
78             Another of App-Context's extended services (App::Widget::*)
79             adds simple and complex active user interface widgets.
80             These widgets can be used to supplement an existing application's
81             user interface technology (template systems, hard-coded HTML, etc.)
82             or the Widget system can be used as the central user interface paradigm.
83              
84             http://www.officevision.com/pub/App-Widget
85              
86             App-Context and its extended service distributions were
87             inspired by work on the Perl 5 Enterprise Environment project,
88             and its goal is to satisfy the all of the requirements embodied in
89             the Attributes of an Enterprise System.
90              
91             See the following web pages for more information about the P5EE project.
92              
93             http://p5ee.perl.org/
94             http://www.officevision.com/pub/p5ee/
95              
96             =head2 Distribution Requirements
97              
98             The following are enumerated requirements for the App-Context distribution.
99             It forms a high-level feature list.
100             The requirements which have been satisfied
101             (or features implemented) have an "x" by them, whereas the requirements
102             which have yet-to-be satisfied have an "o" by them.
103              
104             o an Enterprise Software Architecture, supporting all the Attributes
105             http://www.officevision.com/pub/p5ee/definitions.html
106             o a Software Architecture supporting many Platforms
107             http://www.officevision.com/pub/p5ee/platform.html
108             o a pluggable interface/implementation service architecture
109             o support developers who wish to use portions of the App-Context
110             framework without giving up their other styles of programming
111             (and support gradual migration)
112              
113             =head2 Distribution Design
114              
115             The distribution is designed in such a way that most of the functionality
116             is actually provided by modules outside the App namespace.
117              
118             The goal of the App-Context framework
119             is to bring together many technologies to make a
120             unified whole. In essence, it is collecting and unifying the good work
121             of a multitude of excellent projects which have already been developed.
122             This results in a Pluggable Service design which allows just about
123             everything in App-Context to be customized. These Class Groups are described
124             in detail below.
125              
126             Where a variety of excellent, overlapping or redundant, low-level modules
127             exist on CPAN (i.e. L),
128             a document is
129             written to explain the pros and cons of each.
130              
131             Where uniquely excellent modules exist on CPAN, they are named outright
132             as the standard for the App-Context framework.
133             They are identified as dependencies
134             in the App-Context CPAN Bundle file.
135              
136             =head2 Class Groups
137              
138             The major Class Groups in the App-Context distribution fall into three categories:
139             Core, Core Services, and Services.
140              
141             =over
142              
143             =item * Class Group: L|"Class Group: Core">
144              
145             =item * Class Group: L|App::Context>
146             - encapsulates the runtime environment and the event loop
147              
148             =item * Class Group: L|App::Conf>
149             - retrieve and access configuration information
150              
151             =item * Class Group: L|App::Session>
152             - represents the state associated with a sequence of multiple events
153              
154             =item * Class Group: L|App::Serializer>
155             - transforms a perl struct to a scalar and back
156              
157             =item * Class Group: L|App::Procedure>
158             - a (potentially remote) procedure which may be executed
159              
160             =item * Class Group: L|App::Messaging>
161             - a message queue with configurable quality of service
162              
163             =item * Class Group: L|App::Security>
164             - provides authentication and authorization
165              
166             =item * Class Group: L|App::LogChannel>
167             - a logging channel through which messages may be logged
168              
169             =item * Class Group: L|App::SharedDatastore>
170             - a data storage area which is shared between processes
171              
172             =item * Class Group: L|App::SharedResourceSet>
173             - a set of shared resources which may be locked for exclusive access
174              
175             =back
176              
177             =cut
178              
179             #############################################################################
180             # CLASS GROUP
181             #############################################################################
182              
183             =head1 Class Group: Core
184              
185             The Core Class Group contains the following classes.
186              
187             =over
188              
189             =item * Class: L|"Class: App">
190              
191             =item * Class: L|App::Exceptions>
192              
193             =item * Class: L|App::Reference>
194              
195             =item * Class: L|App::Service>
196              
197             =item * Document: L|App::perlstyle>
198              
199             =item * Document: L|App::podstyle>
200              
201             =item * Document: L|App::datetime>
202              
203             =back
204              
205             =cut
206              
207             #############################################################################
208             # CLASS
209             #############################################################################
210              
211             =head1 Class: App
212              
213             App is the main class through which all of the features
214             of the Perl 5 Enterprise Environment may be accessed.
215              
216             * Throws: Exception::Class::Base
217             * Throws: App::Exception
218             * Throws: App::Exception::Conf
219             * Throws: App::Exception::Context
220             * Since: 0.01
221              
222             =head2 Class Design
223              
224             The class is entirely made up of static (class) methods.
225             There are no constructors for objects of this class itself.
226             Rather, all of the constructors in this package are really
227             factory-style constructors that return objects of different
228             classes.
229             In particular, the new() method is really a synonym for context(),
230             which returns a Context object.
231              
232             =head2 Class Capabilities
233              
234             This class supports the following capabilities.
235              
236             =over
237              
238             =item * Capability: Service Factory
239              
240             This package allows you to construct objects (services) that
241             you do not know
242             the classes for at development time. These classes are specified
243             through the configuration and are produced using this package as
244             a class factory.
245              
246             =back
247              
248             =cut
249              
250             #############################################################################
251             # ATTRIBUTES/CONSTANTS/CLASS VARIABLES/GLOBAL VARIABLES
252             #############################################################################
253              
254             =head1 Attributes, Constants, Global Variables, Class Variables
255              
256             =head2 Global Variables
257              
258             * Global Variable: %App::scope scope for debug or tracing output
259             * Global Variable: $App::scope_exclusive flag saying that the scope is exclusive (a list of things *not* to debug/trace)
260             * Global Variable: %App::trace trace level
261             * Global Variable: $App::DEBUG debug level
262             * Global Variable: $App::DEBUG_FILE file for debug output
263              
264             =cut
265              
266             if (!defined $App::DEBUG) {
267             %App::scope = ();
268             $App::scope_exclusive = 0;
269             $App::trace = 0;
270             $App::DEBUG = 0;
271             }
272              
273             #################################################################
274             # DEBUGGING
275             #################################################################
276              
277             # Supports the following command-line usage:
278             # --debug=1 (global debug)
279             # --debug=9 (detail debug)
280             # --scope=App::Context (debug class only)
281             # --scope=!App::Context (debug all but this class)
282             # --scope=App::Context,App::Session (multiple classes)
283             # --scope=App::Repository::DBI.select_rows (indiv. methods)
284             # --trace=App::Context (trace class only)
285             # --trace=!App::Context (trace all but this class)
286             # --trace=App::Context,App::Session (multiple classes)
287             # --trace=App::Repository::DBI.select_rows (indiv. methods)
288             {
289             my $scope = $App::options{scope} || "";
290              
291             my $trace = $App::options{trace};
292             if ($trace) {
293             if ($trace =~ s/^([0-9]+),?//) {
294             $App::trace = $1;
295             }
296             else {
297             $App::trace = 9;
298             }
299             }
300             if ($trace) {
301             $scope .= "," if ($scope);
302             $scope .= $trace;
303             }
304             $App::trace_width = (defined $App::options{trace_width}) ? $App::options{trace_width} : 1024;
305             $App::trace_justify = (defined $App::options{trace_justify}) ? $App::options{trace_justify} : 0;
306              
307             my $debug = $App::options{debug};
308             if ($debug) {
309             if ($debug =~ s/^([0-9]+),?//) {
310             $App::DEBUG = $1;
311             }
312             else {
313             $App::DEBUG = 9;
314             }
315             }
316             if ($debug) {
317             $scope .= "," if ($scope);
318             $scope .= $debug;
319             }
320              
321             if ($scope =~ s/^!//) {
322             $App::scope_exclusive = 1;
323             }
324              
325             if (defined $scope && $scope ne "") {
326             foreach my $pkg (split(/,/,$scope)) {
327             $App::scope{$pkg} = 1;
328             }
329             }
330              
331             my $debug_file = $App::options{debug_file};
332             if ($debug_file) {
333             if ($debug_file eq "STDOUT") {
334             $App::DEBUG_FILE = \*STDOUT;
335             }
336             elsif ($debug_file eq "STDERR") {
337             $App::DEBUG_FILE = \*STDERR;
338             }
339             else {
340             if ($debug_file !~ /^[>|]/) {
341             $debug_file = "> $debug_file";
342             }
343             if (open(App::DEBUG_FILE_HANDLE, $debug_file)) {
344             $App::DEBUG_FILE = \*App::DEBUG_FILE_HANDLE;
345             }
346             else {
347             warn "WARNING: Couldn't open $debug_file: $!\n";
348             }
349             }
350             }
351             else {
352             $App::DEBUG_FILE = \*STDOUT;
353             }
354             $App::DEBUG_FILE->autoflush(1);
355             }
356              
357             #############################################################################
358             # SUPPORT FOR ASPECT-ORIENTED-PROGRAMMING (AOP)
359             #############################################################################
360              
361             =head1 Code Inclusion and Instrumentation
362              
363             =cut
364              
365             #############################################################################
366             # use()
367             #############################################################################
368              
369             =head2 use()
370              
371             * Signature: App->use($class);
372             * Param: $class string [in]
373             * Return: void
374             * Throws:
375             * Since: 0.01
376              
377             Sample Usage:
378              
379             App->use("App::Widget::Entity");
380              
381             The use() method loads additional perl code and enables aspect-oriented
382             programming (AOP) features if they are appropriate. If these did not
383             need to be turned on or off, it would be easier to simply use the
384             following.
385              
386             eval "use $class;"
387              
388             The first AOP
389             feature planned is the printing of arguments on entry to a method and
390             the printing of arguments and return values on exit of a a method.
391              
392             This is useful
393             for debugging and the generation of object-message traces to validate
394             or document the flow of messages through the system.
395              
396             Detailed Conditions:
397              
398             * use(001) class does not exist: throw a App::Exception
399             * use(002) class never used before: should succeed
400             * use(003) class used before: should succeed
401             * use(004) can use class after: should succeed
402              
403             =cut
404              
405             my (%used);
406              
407             sub use {
408 33 50   33 1 6917 &App::sub_entry if ($App::trace);
409 33         65 my ($self, $class) = @_;
410 8     8   86 no strict; # allow fiddling with the symbol table
  8         16  
  8         37135  
411 33 100       159 if (! defined $used{$class}) {
412             # if we try to use() it again, we won't get an exception
413 31         73 $used{$class} = 1;
414              
415             # I could look for a particular variable like $VERSION,
416             # local (*VERSION) = ${*{"$class\::"}}{VERSION};
417             # print "$class VERSION: ", ${*VERSION{SCALAR}}, "\n";
418              
419             # but I decided to look for *any* symbol table entry instead.
420 31 100       43 if (%{*{"$class\::"}}) { # if any symbols exist in the symbol table
  31 50       66  
  31         398  
421             # do nothing
422             }
423             elsif ($class =~ /^([A-Za-z0-9_:]+)$/) {
424 6     6   4201 eval "use $1;";
  6     6   41  
  6     6   132  
  6     6   3544  
  6     3   16  
  6         107  
  6         3984  
  6         15  
  6         131  
  6         3288  
  5         400  
  5         104  
  3         2375  
  3         9  
  3         63  
  29         2837  
425 29 100       173 if ($@) {
426 2         45 App::Exception->throw(
427             error => "class $class failed to load: $@\n",
428             );
429             }
430             }
431             else {
432 0         0 App::Exception->throw(
433             error => "Tried to load class [$class] with illegal characters\n",
434             );
435             }
436             }
437 31 50       245 &App::sub_exit() if ($App::trace);
438             }
439              
440             # $dir = App->mkdir($prefix, "data", "app", "Context");
441             sub mkdir {
442 0 0   0 0 0 &App::sub_entry if ($App::trace);
443 0         0 my ($self, @dirs) = @_;
444              
445 0         0 my $dir = shift(@dirs);
446 0 0       0 if ($dir) {
447 0 0       0 mkdir($dir) if (! -d $dir);
448 0         0 foreach my $d (@dirs) {
449 0         0 $dir = File::Spec->catdir($dir, $d);
450 0 0       0 mkdir($dir) if (! -d $dir);
451             }
452             }
453 0 0       0 &App::sub_exit($dir) if ($App::trace);
454 0         0 return($dir);
455             }
456              
457             #############################################################################
458             # printargs()
459             #############################################################################
460              
461             =head2 printargs()
462              
463             * Signature: App->printargs($depth, $skipatend, @args);
464             * Param: $depth integer [in]
465             * Param: $skipatend integer [in]
466             * Param: @args any [in]
467             * Return: void
468             * Throws: none
469             * Since: 0.01
470              
471             =cut
472              
473             sub printargs {
474 0     0 1 0 my $depth = shift;
475 0         0 my $skipatend = shift;
476 0         0 my ($narg);
477 0         0 for ($narg = 0; $narg <= $#_ - $skipatend; $narg++) {
478 0 0       0 print "," if ($narg);
479 0 0       0 if (ref($_[$narg]) eq "") {
    0          
    0          
480 0         0 print $_[$narg];
481             }
482             elsif (ref($_[$narg]) eq "ARRAY") {
483 0         0 print "[";
484 0 0       0 if ($depth <= 1) {
485 0         0 print join(",", @{$_[$narg]});
  0         0  
486             }
487             else {
488 0         0 &printdepth($depth-1, 0, @{$_[$narg]});
  0         0  
489             }
490 0         0 print "]";
491             }
492             elsif (ref($_[$narg]) eq "HASH") {
493 0         0 print "{";
494 0 0       0 if ($depth <= 1) {
495 0         0 print join(",", %{$_[$narg]});
  0         0  
496             }
497             else {
498 0         0 &printdepth($depth-1, 0, %{$_[$narg]});
  0         0  
499             }
500 0         0 print "}";
501             }
502             else {
503 0         0 print $_[$narg];
504             }
505             }
506             }
507              
508             #############################################################################
509             # CONSTRUCTOR METHODS
510             #############################################################################
511              
512             =head1 Constructor Methods:
513              
514             =cut
515              
516             #############################################################################
517             # new()
518             #############################################################################
519              
520             =head2 new()
521              
522             The App->new() method is not a constructor for
523             an App class. Rather, it is a Factory-style constructor, returning
524             an object of the class given as the first parameter.
525              
526             If no parameters are given,
527             it is simply a synonym for "App->context()".
528              
529             * Signature: $context = App->new()
530             * Signature: $object = App->new($class)
531             * Signature: $object = App->new($class,$method)
532             * Signature: $object = App->new($class,$method,@args)
533             * Param: $class class [in]
534             * Param: $method string [in]
535             * Return: $context App::Context
536             * Return: $object ref
537             * Throws: Exception::Class::Base
538             * Since: 0.01
539              
540             Sample Usage:
541              
542             $context = App->new();
543             $dbh = App->new("DBI", "new", "dbi:mysql:db", "dbuser", "xyzzy");
544             $cgi = App->new("CGI", "new");
545              
546             =cut
547              
548             sub new {
549 19 50   19 1 60 &App::sub_entry if ($App::trace);
550 19         35 my $self = shift;
551 19 50       76 if ($#_ == -1) {
552 0         0 my $context = $self->context();
553 0 0       0 &App::sub_exit($context) if ($App::trace);
554 0         0 return($context);
555             }
556 19         32 my $class = shift;
557 19 50       161 if ($class =~ /^([A-Za-z0-9:_]+)$/) {
558 19         56 $class = $1; # untaint the $class
559 19 50       62 if (! $used{$class}) {
560 19         68 $self->use($class);
561             }
562 19 50       99 my $method = ($#_ > -1) ? shift : "new";
563 19 50       61 if (wantarray) {
564 0         0 my @values = $class->$method(@_);
565 0 0       0 &App::sub_exit(@values) if ($App::trace);
566 0         0 return(@values);
567             }
568             else {
569 19         156 my $value = $class->$method(@_);
570 19 50       54 &App::sub_exit($value) if ($App::trace);
571 19         89 return($value);
572             }
573             }
574 0         0 print STDERR "Illegal Class Name: [$class]\n";
575 0 0       0 &App::sub_exit(undef) if ($App::trace);
576 0         0 return undef;
577             }
578              
579             #############################################################################
580             # context()
581             #############################################################################
582              
583             =head2 context()
584              
585             * Signature: $context = App->context(); # most common, used in "app"
586             * Signature: $context = App->context(%named); # also used
587             * Signature: $context = App->context($named, %named); # variation
588             * Signature: $context = App->context($name, %named); # rare
589             * Signature: $context = App->context($named, $name, %named); # rare
590             * Param: context_class class [in]
591             * Param: config_file string [in]
592             * Param: prefix string [in]
593             * Return: $context App::Context
594             * Throws: App::Exception::Context
595             * Since: 0.01
596              
597             Sample Usage:
598              
599             $context = App->context();
600             $context = App->context(
601             context_class => "App::Context::HTTP",
602             config_file => "app.xml",
603             );
604              
605             This static (class) method returns the $context object
606             of the context in which you are running.
607             It tries to use some intelligence in determining which
608             context is the right one to instantiate, although you
609             can override it explicitly.
610              
611             It implements a "Factory" design pattern. Instead of using the
612             constructor of a class itself to get an instance of that
613             class, the context() method of App is used. The former
614             technique would require us to know at development time
615             which class was to be instantiated. Using the factory
616             style constructor, the developer need not ever know what physical class
617             is implementing the "Context" interface. Rather, it is
618             configured at deployment-time, and the proper physical class
619             is instantiated at run-time.
620              
621             The new() method of the configured Context class is called to
622             instantiate the proper Context object. The $named args are
623             combined with the %named args and passed as a single hash
624             reference to the new() method.
625              
626             Environment variables:
627              
628             PREFIX - set the $conf->{prefix} variable if not set to set app root dir
629             APP_CONTEXT_CLASS - set the Perl module to instantiate for the Context
630             GATEWAY_INTERFACE - assume mod_perl, use App::Context::ModPerl
631             HTTP_USER_AGENT - assume CGI, use App::Context::HTTP
632             (otherwise, use App::Context::Cmd, assuming it is from command line)
633              
634             =cut
635              
636             my (%context); # usually a singleton per process (under "default" name)
637             # multiple named contexts are allowed for debugging purposes
638              
639             sub context {
640 8 50   8 1 4931 &App::sub_entry if ($App::trace);
641 8         18 my $self = shift;
642              
643 8         19 my ($name, $options, $i);
644 8 100       38 if ($#_ == -1) { # if no options supplied (the normal case)
645 3 100       13 $options = (%App::options) ? \%App::options : {}; # options hash
646 3         8 $name = "default"; # name of the singleton is default
647             }
648             else { # named args were supplied ...
649 5 50       201 if (ref($_[0]) eq "HASH") { # ... as a hash reference
650 0         0 $options = shift; # note that a copy is *not* made
651 0         0 for ($i = 0; $i < $#_; $i++) { # copy other named args
652 0         0 $options->{$_[$i]} = $_[$i+1]; # into the options hash
653             }
654             }
655             else { # ... as a list of var/values
656 5 50       28 $name = shift if ($#_ % 2 == 0); # if odd #, first is the name
657 5 50       34 $options = ($#_ > -1) ? { @_ } : {}; # the rest are named args
658             }
659 5 50       24 $name = $options->{name} if (!$name); # if not given, look in options
660 5 50       42 $name = "default" if (!$name); # use "default" as name
661             }
662              
663 8 100       31 if (!defined $context{$name}) {
664            
665 6 100       33 if (! $options->{context_class}) {
666 5 50       27 if (defined $ENV{APP_CONTEXT_CLASS}) { # env variable set?
667 0         0 $options->{context_class} = $ENV{APP_CONTEXT_CLASS};
668             }
669             else { # try autodetection ...
670 5 50       31 if ($ENV{MOD_PERL}) {
    50          
671 0         0 $options->{context_class} = "App::Context::ModPerl";
672             }
673             elsif ($ENV{HTTP_USER_AGENT}) { # running as CGI script?
674 0         0 $options->{context_class} = "App::Context::HTTP";
675             }
676             else { # assume it is from the command line
677 5         33 $options->{context_class} = "App::Context::Cmd";
678             }
679             }
680             }
681 6 50       23 if (!$options->{prefix}) { # if this isn't already set
682 6 50       25 if ($ENV{PREFIX}) { # but it's set in the environment
683 0         0 $options->{prefix} = $ENV{PREFIX}; # then set it
684             }
685             }
686              
687             # instantiate Context and cache it (it's reference) for future use
688 6         33 $context{$name} = $self->new($options->{context_class}, "new", $options);
689             }
690              
691 8 50       33 &App::sub_exit($context{$name}) if ($App::trace);
692 8         57 return($context{$name});
693             }
694              
695             sub shutdown {
696 1 50   1 0 420 &App::sub_entry if ($App::trace);
697 1         3 my ($self, $name) = @_;
698 1 50       5 $name = "default" if (!defined $name);
699 1 50       14 $context{$name}->shutdown() if (defined $context{$name});
700 1         2 delete $context{$name};
701 1 50       4 &App::sub_exit() if ($App::trace);
702             }
703              
704             #############################################################################
705             # conf()
706             #############################################################################
707              
708             =head2 conf()
709              
710             * Signature: $conf = App->conf(%named);
711             * Param: conf_class class [in]
712             * Param: config_file string [in]
713             * Return: $conf App::Conf
714             * Throws: App::Exception::Conf
715             * Since: 0.01
716              
717             This gets the Conf object from the Context.
718              
719             If args are passed in, they are only effective in affecting the Context
720             if the Context has not been instantiated before.
721              
722             After the Context is instantiated by either the App->context() call or the
723             App->conf() call, then subsequent calls to either method may or may not
724             include arguments. It will not have any further effect because the
725             Context object instantiated earlier will be used.
726              
727             =cut
728              
729             sub conf {
730 2 50   2 1 1309 &App::sub_entry if ($App::trace);
731 2         6 my $self = shift;
732 2         11 my $retval = $self->context(@_)->conf();
733 2 50       7 &App::sub_exit($retval) if ($App::trace);
734 2         9 $retval;
735             }
736              
737             #############################################################################
738             # info()
739             #############################################################################
740              
741             =head2 info()
742              
743             * Signature: $ident = App->info();
744             * Param: void
745             * Return: $ident string
746             * Throws: App::Exception
747             * Since: 0.01
748              
749             Gets version info about the framework.
750              
751             =cut
752              
753             sub info {
754 0 0   0 1   &App::sub_entry if ($App::trace);
755 0           my $self = shift;
756 0           my $retval = "App-Context ($App::VERSION)";
757 0 0         &App::sub_exit($retval) if ($App::trace);
758 0           return($retval);
759             }
760              
761             #############################################################################
762             # Aspect-oriented programming support
763             #############################################################################
764             # NOTE: This can be done much more elegantly at the Perl language level,
765             # but it requires version-specific code. I created these subroutines so that
766             # any method that is instrumented with them will enable aspect-oriented
767             # programming in Perl versions from 5.5.3 to the present.
768             #############################################################################
769              
770             my $calldepth = 0;
771              
772             #############################################################################
773             # sub_entry()
774             #############################################################################
775              
776             =head2 sub_entry()
777              
778             * Signature: &App::sub_entry;
779             * Signature: &App::sub_entry(@args);
780             * Param: @args any
781             * Return: void
782             * Throws: none
783             * Since: 0.01
784              
785             This is called at the beginning of a subroutine or method (even before $self
786             may be shifted off).
787              
788             =cut
789              
790             sub sub_entry {
791 0 0   0 1   if ($App::trace) {
792 0           my ($stacklevel, $calling_package, $file, $line, $subroutine, $hasargs, $wantarray, $text);
793 0           $stacklevel = 1;
794 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
795 0   0       while (defined $subroutine && $subroutine eq "(eval)") {
796 0           $stacklevel++;
797 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
798             }
799 0           my ($name, $obj, $class, $package, $sub, $method, $firstarg, $trailer);
800              
801             # split subroutine into its "package" and the "sub" within the package
802 0 0         if ($subroutine =~ /^(.*)::([^:]+)$/) {
803 0           $package = $1;
804 0           $sub = $2;
805             }
806              
807             # check if it might be a method call rather than a normal subroutine call
808 0 0         if ($#_ >= 0) {
809 0           $class = ref($_[0]);
810 0 0         if ($class) {
811 0           $obj = $_[0];
812 0 0 0       $method = $sub if ($class ne "ARRAY" && $class ne "HASH");
813             }
814             else {
815 0           $class = $_[0];
816 0 0 0       if ($class =~ /^[A-Z][A-Za-z0-9_:]*$/ && $class->isa($package)) {
817 0           $method = $sub; # the sub is a method call on the class
818             }
819             else {
820 0           $class = ""; # it wasn't really a class/method
821             }
822             }
823             }
824              
825 0 0         if (%App::scope) {
826 0 0         if ($App::scope_exclusive) {
827 0 0 0       return if ($App::scope{$package} || $App::scope{"$package.$sub"});
828             }
829             else {
830 0 0 0       return if (!$App::scope{$package} && !$App::scope{"$package.$sub"});
831             }
832             }
833              
834 0 0         if ($method) {
835 0 0         if (ref($obj)) { # dynamic method, called on an object
836 0 0         if ($obj->isa("App::Service")) {
837 0           $text = ("| " x $calldepth) . "+-" . $obj->{name} . "->${method}(";
838             }
839             else {
840 0           $text = ("| " x $calldepth) . "+-" . $obj . "->${method}(";
841             }
842 0           $trailer = " [$package]";
843             }
844             else { # static method, called on a class
845 0           $text = ("| " x $calldepth) . "+-" . "${class}->${method}(";
846 0 0         $trailer = ($class eq $package) ? "" : " [$package]";
847             }
848 0           $firstarg = 1;
849             }
850             else {
851 0           $text = ("| " x $calldepth) . "+-" . $subroutine . "(";
852 0           $firstarg = 0;
853 0           $trailer = "";
854             }
855 0           my ($narg);
856 0           for ($narg = $firstarg; $narg <= $#_; $narg++) {
857 0 0         $text .= "," if ($narg > $firstarg);
858 0 0         if (!defined $_[$narg]) {
    0          
    0          
    0          
859 0           $text .= "undef";
860             }
861             elsif (ref($_[$narg]) eq "") {
862 0           $text .= $_[$narg];
863             }
864             elsif (ref($_[$narg]) eq "ARRAY") {
865 0 0         $text .= ("[" . join(",", map { defined $_ ? $_ : "undef" } @{$_[$narg]}) . "]");
  0            
  0            
866             }
867             elsif (ref($_[$narg]) eq "HASH") {
868 0 0         $text .= ("{" . join(",", map { defined $_ ? $_ : "undef" } %{$_[$narg]}) . "}");
  0            
  0            
869             }
870             else {
871 0           $text .= $_[$narg];
872             }
873             }
874             #$trailer .= " [package=$package sub=$sub subroutine=$subroutine class=$class method=$method]";
875 0           $text .= ")";
876 0           my $trailer_len = length($trailer);
877 0           $text =~ s/\n/\\n/g;
878 0           my $text_len = length($text);
879 0 0         if ($App::trace_width) {
880 0 0         if ($text_len + $trailer_len > $App::trace_width) {
    0          
881 0           my $len = $App::trace_width - $trailer_len;
882 0 0         $len = 1 if ($len < 1);
883 0           print $App::DEBUG_FILE substr($text, 0, $len), $trailer, "\n";
884             }
885             elsif ($App::trace_justify) {
886 0           my $len = $App::trace_width - $trailer_len - $text_len;
887 0 0         $len = 0 if ($len < 0); # should never happen
888 0           print $App::DEBUG_FILE $text, ("." x $len), $trailer, "\n";
889             }
890             else {
891 0           print $App::DEBUG_FILE $text, $trailer, "\n";
892             }
893             }
894             else {
895 0           print $App::DEBUG_FILE $text, $trailer, "\n";
896             }
897 0           $calldepth++;
898             }
899             }
900              
901             #############################################################################
902             # sub_exit()
903             #############################################################################
904              
905             =head2 sub_exit()
906              
907             * Signature: &App::sub_exit(@return);
908             * Param: @return any
909             * Return: void
910             * Throws: none
911             * Since: 0.01
912              
913             This subroutine is called just before you return from a subroutine or method.
914             =cut
915              
916             sub sub_exit {
917 0 0   0 1   if ($App::trace) {
918 0           my ($stacklevel, $calling_package, $file, $line, $subroutine, $hasargs, $wantarray, $text);
919 0           $stacklevel = 1;
920 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
921 0   0       while (defined $subroutine && $subroutine eq "(eval)") {
922 0           $stacklevel++;
923 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
924             }
925              
926 0           my ($package, $sub);
927             # split subroutine into its "package" and the "sub" within the package
928 0 0         if ($subroutine =~ /^(.*)::([^:]+)$/) {
929 0           $package = $1;
930 0           $sub = $2;
931             }
932              
933 0 0 0       return if (%App::scope && !$App::scope{$package} && !$App::scope{"$package.$sub"});
      0        
934              
935 0           $calldepth--;
936 0           $text = ("| " x $calldepth) . "+-> $sub()";
937 0           my ($narg, $arg);
938 0           for ($narg = 0; $narg <= $#_; $narg++) {
939 0 0         $text .= $narg ? "," : " : ";
940 0           $arg = $_[$narg];
941 0 0         if (! defined $arg) {
    0          
    0          
    0          
942 0           $text .= "undef";
943             }
944             elsif (ref($arg) eq "") {
945 0           $text .= $arg;
946             }
947             elsif (ref($arg) eq "ARRAY") {
948 0 0         $text .= ("[" . join(",", map { defined $_ ? $_ : "undef" } @$arg) . "]");
  0            
949             }
950             elsif (ref($arg) eq "HASH") {
951 0 0         $text .= ("{" . join(",", map { defined $_ ? $_ : "undef" } %$arg) . "}");
  0            
952             }
953             else {
954 0 0         $text .= defined $arg ? $arg : "undef";
955             }
956             }
957 0           $text =~ s/\n/\\n/g;
958 0 0 0       if ($App::trace_width && length($text) > $App::trace_width) {
959 0           print $App::DEBUG_FILE substr($text, 0, $App::trace_width), "\n";
960             }
961             else {
962 0           print $App::DEBUG_FILE $text, "\n";
963             }
964             }
965 0           return(@_);
966             }
967              
968             #############################################################################
969             # in_debug_scope()
970             #############################################################################
971              
972             =head2 in_debug_scope()
973              
974             * Signature: &App::in_debug_scope
975             * Signature: App->in_debug_scope
976             * Param:
977             * Return: void
978             * Throws: none
979             * Since: 0.01
980              
981             This is called within a subroutine or method in order to see if debug output
982             should be produced.
983              
984             if ($App::debug && &App::in_debug_scope) {
985             print "This is debug output\n";
986             }
987              
988             Note: The App::in_debug_scope subroutine also checks $App::debug, but checking
989             it in your code allows you to skip the subroutine call if you are not debugging.
990              
991             if (&App::in_debug_scope) {
992             print "This is debug output\n";
993             }
994              
995             =cut
996              
997             sub in_debug_scope {
998 0 0   0 1   if ($App::debug) {
999 0           my ($stacklevel, $calling_package, $file, $line, $subroutine, $hasargs, $wantarray, $text);
1000 0           $stacklevel = 1;
1001 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
1002 0   0       while (defined $subroutine && $subroutine eq "(eval)") {
1003 0           $stacklevel++;
1004 0           ($calling_package, $file, $line, $subroutine, $hasargs, $wantarray) = caller($stacklevel);
1005             }
1006 0           my ($package, $sub);
1007              
1008             # split subroutine into its "package" and the "sub" within the package
1009 0 0         if ($subroutine =~ /^(.*)::([^:]+)$/) {
1010 0           $package = $1;
1011 0           $sub = $2;
1012             }
1013              
1014 0 0         if (%App::scope) {
1015 0 0         if ($App::scope_exclusive) {
1016 0 0 0       return(undef) if ($App::scope{$package} || $App::scope{"$package.$sub"});
1017             }
1018             else {
1019 0 0 0       return(undef) if (!$App::scope{$package} && !$App::scope{"$package.$sub"});
1020             }
1021             }
1022 0           return(1);
1023             }
1024 0           return(undef);
1025             }
1026              
1027             #############################################################################
1028             # debug_indent()
1029             #############################################################################
1030              
1031             =head2 debug_indent()
1032              
1033             * Signature: &App::debug_indent()
1034             * Signature: App->debug_indent()
1035             * Param: void
1036             * Return: $indent_str string
1037             * Throws: none
1038             * Since: 0.01
1039              
1040             This subroutine returns the $indent_str string which should be printed
1041             before all debug lines if you wish to line the debug output up with the
1042             nested/indented trace output.
1043              
1044             =cut
1045              
1046             sub debug_indent {
1047 0     0 1   my $text = ("| " x $calldepth) . " * ";
1048 0           return($text);
1049             }
1050              
1051             =head1 ACKNOWLEDGEMENTS
1052              
1053             * Author: Stephen Adkins
1054             * License: This is free software. It is licensed under the same terms as Perl itself.
1055              
1056             =head1 SEE ALSO
1057              
1058             =cut
1059              
1060             1;
1061