File Coverage

blib/lib/Hadoop/Inline/ClassLoader.pm
Criterion Covered Total %
statement 29 116 25.0
branch 0 26 0.0
condition 0 16 0.0
subroutine 10 15 66.6
pod n/a
total 39 173 22.5


line stmt bran cond sub pod time code
1             package Hadoop::Inline::ClassLoader;
2              
3 1     1   143369 use 5.014;
  1         3  
4 1     1   4 use strict;
  1         1  
  1         18  
5 1     1   4 use warnings;
  1         1  
  1         82  
6              
7             our $VERSION = '0.002'; # VERSION
8              
9 1     1   5 use Carp qw( croak );
  1         1  
  1         60  
10 1     1   4 use File::Basename qw( dirname );
  1         1  
  1         49  
11 1         78 use Ref::Util qw(
12             is_arrayref
13             is_hashref
14 1     1   506 );
  1         2184  
15              
16             use Constant::FromGlobal
17 1         7 DEBUG => {
18             default => 0,
19             env => 1,
20             int => 1,
21             },
22 1     1   406 ;
  1         7880  
23              
24             use constant {
25 1         928 CHAR_COLON => q{:},
26             EMPTY_STRING => q{ },
27             HADOOP_COMMAND => '/usr/bin/hadoop',
28             IJ_DEBUG => DEBUG && DEBUG > 1,
29             PACKAGE_DELIMITER => q{::},
30             RE_MULTI_LF => qr{ \n+ }xms,
31             RE_PATH_SEP_CHAR => qr{ [:] }xms,
32             RE_WS => qr{ \s+ }xms,
33 1     1   270 };
  1         2  
34              
35             my $ENV_INITIALIZED;
36              
37             my %DEFAULT = (
38             alias => 1,
39             );
40              
41             sub import {
42 0     0     my($class, @args) = @_;
43              
44 0 0 0       my $opt = @args && is_hashref $args[0] ? shift(@args) : { %DEFAULT };
45 0 0         my @java_classes = @args or croak 'No java classes were defined';
46 0   0       my $caller = $opt->{export_to} || caller 1;
47              
48 0 0         if ( ! $ENV_INITIALIZED ) {
49 0           my($henv, $paths) = $class->_collect_env( $opt );
50 0           $ENV{$_} = $henv->{ $_ } for keys %{ $henv };
  0            
51              
52 0           require Inline;
53 0           require Inline::Java;
54              
55 0           Inline->import(
56             Java => 'STUDY',
57             STUDY => [],
58             DEBUG => IJ_DEBUG,
59             );
60              
61 0           $ENV_INITIALIZED++;
62             }
63              
64             eval qq{
65             package $caller;
66             use Inline (
67             Java => 'STUDY',
68             STUDY => [],
69             ) ;
70             1;
71 0 0         } or do {
72 0   0       my $eval_error = $@ || 'Zombie error';
73 0           croak sprintf 'Unable to inject Inline::Java into the caller(%s): %s',
74             $caller,
75             $eval_error,
76             ;
77             };
78              
79             # TODO: further checks on the user params?
80 0           Inline::Java::study_classes( \@java_classes , $caller );
81              
82 0 0         $class->_map_java_imports_to_short_names if $opt->{alias};
83              
84 0           return;
85             }
86              
87             sub _capture {
88 0     0     my @cmd = @_;
89 0           my $rv = qx{@cmd};
90 0 0         croak "Failed to execute `@cmd`" if $?;
91 0           chomp $rv;
92 0 0         croak "Nothing returned from `@cmd`" if ! $rv;
93 0           return $rv;
94             }
95              
96             sub _collect_env {
97 0     0     my $class = shift;
98 0           my $opt = shift;
99              
100 0 0         croak 'Options need to be a HASH' if ! is_hashref $opt;
101              
102 0   0       my $cmd = $opt->{hadoop_command} || HADOOP_COMMAND;
103              
104 0 0 0       if ( ! -e $cmd || ! -x _ ) {
105 0           croak sprintf 'This module requires `%s` to be present as an executable',
106             $cmd,
107             ;
108             }
109              
110 0           my $q = _capture( $cmd => 'classpath' );
111              
112 0           my @paths = split RE_PATH_SEP_CHAR, $q;
113              
114 0 0         push @paths, @{ $opt->{extra_classpath} } if is_arrayref $opt->{extra_classpath};
  0            
115              
116 0           if ( DEBUG ) {
117             print STDERR 'CLASSPATH(before expansion):\n';
118             print STDERR "\t$_\n" for @paths;
119             }
120              
121             # java expects the shell expansion to happen outside as passing the paths
122             # with meta characters don't do anything.
123             #
124             # In short; CLASSPATH is not identical to PERL5LIB and this manual work is
125             # needed. It is possible that we may hit some shell limitation on this
126             # variable if too many things are pushed above.
127             #
128 0           @paths = map { glob $_ } @paths;
  0            
129              
130 0           if ( DEBUG ) {
131             print STDERR 'CLASSPATH(after expansion):\n';
132             print STDERR "\t$_\n" for @paths;
133             }
134              
135 0           state $re_path_sep = RE_PATH_SEP_CHAR;
136 0           my %n = do {
137 0           my $native = _capture( $cmd => 'checknative' );
138 0           my @n = split RE_MULTI_LF, $native;
139 0           shift @n;
140             map {
141 0           my @k = split RE_WS, $_, 3; ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
  0            
142 0           $k[0] =~ s{ $re_path_sep \z }{}xms;
143 0           $k[0] => {
144             status => $k[1],
145             value => $k[2],
146             };
147             } @n;
148             };
149              
150 0   0       my $hadoop = $n{hadoop} || croak 'Failed to collect the hadoop native binary path';
151 0           my $native = dirname $hadoop->{value};
152 0           my $hopts = '-Djava.library.path=' . dirname $native;
153              
154 0           my %henv;
155 0           $henv{CLASSPATH} = join CHAR_COLON, @paths;
156 0           $henv{HADOOP_COMMON_LIB_NATIVE_DIR} = $native;
157             $henv{HADOOP_OPTS} = $ENV{HADOOP_OPTS}
158 0 0         ? $ENV{HADOOP_OPTS} . EMPTY_STRING . $hopts
159             : $hopts
160             ;
161              
162             # if LD_LIBRARY_PATH is not set:
163             #
164             # util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
165              
166 0           for my $path (qw/
167             JAVA_LIBRARY_PATH
168             LD_LIBRARY_PATH
169             /) {
170 0 0         $henv{ $path } = $ENV{ $path } ? $ENV{ $path } . CHAR_COLON . $native : $native;
171             }
172              
173 0           return \%henv, \@paths;
174             }
175              
176             sub _map_java_imports_to_short_names {
177 0     0     my $class = shift;
178             # Give somewhat meaningful names to the imported Java classes
179              
180 0           state $package_delimiter = PACKAGE_DELIMITER;
181              
182 0           my $base_ns = 'org::apache::hadoop::';
183 0           my $filter = do {
184 0           my @rv = split m{ $package_delimiter }xms, $base_ns;
185 0           pop @rv;
186 0           join PACKAGE_DELIMITER, @rv;
187             };
188              
189 0           my @ns = $class->_namespace_probe( $base_ns );
190 0           shift @ns;
191              
192 0           @ns = grep { m{ $package_delimiter \z }xms } @ns;
  0            
193              
194 1     1   7 no strict qw( refs );
  1         1  
  1         224  
195 0           foreach my $class ( @ns ) {
196 0           (my $short_name = $class) =~ s{
197             \Q$filter\E
198             $package_delimiter
199             }{}xms;
200             $short_name = join PACKAGE_DELIMITER,
201 0           map { ucfirst $_ } split m{ $package_delimiter }xms,
  0            
202             $short_name
203             ;
204             # import() was called multiple times?
205 0 0         next if %{ $short_name . PACKAGE_DELIMITER };
  0            
206              
207 0           printf STDERR 'Mapping %s => %s', $short_name, $class if DEBUG;
208              
209 0           *{ $short_name . PACKAGE_DELIMITER } = \*{ $class };
  0            
  0            
210             }
211              
212 0           return;
213             }
214              
215             sub _namespace_probe {
216 0     0     my $class = shift;
217 0           my $sym = shift;
218              
219 0           my @names;
220 1     1   5 no strict qw( refs );
  1         1  
  1         136  
221 0           foreach my $type (
222             grep {
223 0           m{ \A [a-z] }xmsi
224             }
225 0           keys %{ $sym }
226             ) {
227 0           push @names, $class->_namespace_probe( $sym . $type );
228             }
229              
230 0           return $sym, @names;
231             }
232              
233             1;
234              
235             __END__