File Coverage

blib/lib/Lim/RPC/Server.pm
Criterion Covered Total %
statement 195 320 60.9
branch 62 194 31.9
condition 11 45 24.4
subroutine 23 26 88.4
pod 9 9 100.0
total 300 594 50.5


line stmt bran cond sub pod time code
1             package Lim::RPC::Server;
2              
3 7     7   573458 use common::sense;
  7         54  
  7         50  
4 7     7   422 use Carp;
  7         16  
  7         641  
5              
6 7     7   45 use Log::Log4perl ();
  7         14  
  7         169  
7 7     7   47 use Scalar::Util qw(blessed weaken);
  7         14  
  7         572  
8              
9 7     7   4619 use URI ();
  7         49614  
  7         175  
10 7     7   6789 use URI::Split ();
  7         5485  
  7         162  
11              
12 7     7   3487 use Lim ();
  7         20  
  7         188  
13 7     7   3052 use Lim::RPC ();
  7         18  
  7         162  
14 7     7   3181 use Lim::RPC::Value ();
  7         21  
  7         151  
15 7     7   3480 use Lim::RPC::Value::Collection ();
  7         18  
  7         146  
16 7     7   4422 use Lim::RPC::Protocols ();
  7         117  
  7         176  
17 7     7   4779 use Lim::RPC::Transports ();
  7         20  
  7         207  
18 7     7   3782 use Lim::RPC::URIMaps ();
  7         24  
  7         32122  
19              
20             =encoding utf8
21              
22             =head1 NAME
23              
24             ...
25              
26             =head1 VERSION
27              
28             See L for version.
29              
30             =cut
31              
32             our $VERSION = $Lim::VERSION;
33              
34             =head1 SYNOPSIS
35              
36             ...
37              
38             =head1 SUBROUTINES/METHODS
39              
40             =head2 new
41              
42             =cut
43              
44             sub new {
45 2     2 1 84598 my $this = shift;
46 2   33     79 my $class = ref($this) || $this;
47 2         55 my %args = ( @_ );
48 2         284 my $self = {
49             logger => Log::Log4perl->get_logger,
50             protocol => {},
51             transports => [],
52             module => {}
53             };
54 2         1139 bless $self, $class;
55              
56 2 50       14 unless (defined $args{uri}) {
57 0         0 confess __PACKAGE__, ': No uri specified';
58             }
59              
60 2 50       33 foreach my $uri (ref($args{uri}) eq 'ARRAY' ? @{$args{uri}} : $args{uri}) {
  0         0  
61 2         66 my ($scheme, $auth, $path, $query, $frag) = URI::Split::uri_split($uri);
62              
63 2 50       127 if ($scheme =~ /^([a-z0-9_\-\.]+)(?:\+([a-z0-9_\-\.\+]+))*/o) {
64 2         19 my ($transport_name, $protocols) = ($1, $2);
65 2         15 my (@protocols, $transport);
66 2         95 $uri = URI->new('', 'http');
67 2         26829 $uri->query($query);
68 2         671 $uri->host_port($auth);
69            
70 2         406 foreach my $protocol_name (split(/\+/o, $protocols)) {
71 2 50       58 unless (exists $self->{protocol}->{$protocol_name}) {
72 2         5 my $protocol;
73              
74 2 50       113 unless (defined ($protocol = Lim::RPC::Protocols->instance->protocol($protocol_name, server => $self))) {
75 0         0 confess __PACKAGE__, ': Protocol ', $protocol_name, ' does not exists';
76             }
77            
78 2         11 $self->{protocol}->{$protocol_name} = $protocol;
79 2         11 push(@protocols, $protocol);
80             }
81             else {
82 0         0 push(@protocols, $self->{protocol}->{$protocol_name});
83             }
84             }
85            
86 2 50       58 unless (defined ($transport = Lim::RPC::Transports->instance->transport($transport_name, server => $self, uri => $uri))) {
87 0         0 confess __PACKAGE__, ': Transport ', $transport_name, ' does not exists';
88             }
89            
90 2         31 $transport->add_protocol(@protocols);
91 2         3 push(@{$self->{transports}}, $transport);
  2         33  
92             }
93             else {
94 0         0 confess __PACKAGE__, ': Unable to parse URI schema: ', $uri;
95             }
96             }
97              
98 2 50       12 Lim::OBJ_DEBUG and $self->{logger}->debug('new ', __PACKAGE__, ' ', $self);
99 2         1023 $self;
100             }
101              
102             sub DESTROY {
103 2     2   2309 my ($self) = @_;
104 2 50       12 Lim::OBJ_DEBUG and $self->{logger}->debug('destroy ', __PACKAGE__, ' ', $self);
105              
106 2         1560 delete $self->{transports};
107 2         300 delete $self->{module};
108 2         39 delete $self->{protocol};
109             }
110              
111             =head2 serve
112              
113             =cut
114              
115             sub serve {
116 2     2 1 22 my ($self) = shift;
117              
118 2         26 foreach my $module (@_) {
119 2         4 my $obj;
120              
121 2         4 eval {
122 2         1689 $obj = $module->Server;
123             };
124 2 50 33     55 if (!defined $obj or $@) {
125 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $module, (defined $@ ? ': '.$@ : ''));
    0          
126 0         0 next;
127             }
128              
129 2 50       43 if ($obj->isa('Lim::Component::Server')) {
130 2         120 my $name = lc($module->Name);
131            
132 2 50       16 if (exists $self->{module}->{$name}) {
133 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': plugin already served');
134 0         0 next;
135             }
136            
137 2 50       46 unless ($module->VERSION) {
138 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': no VERSION specified in plugin');
139 0         0 next;
140             }
141            
142 2         30 my $calls = $module->Calls;
143 2 50       13 unless ($calls) {
144 0 0       0 Lim::INFO and $self->{logger}->info('Not serving ', $name, ', nothing to serve');
145 0         0 next;
146             }
147 2 50       14 unless (ref($calls) eq 'HASH') {
148 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': Calls() return was invalid');
149 0         0 next;
150             }
151 2 50       18 unless (%$calls) {
152 0 0       0 Lim::INFO and $self->{logger}->info('Not serving ', $name, ', nothing to serve');
153 0         0 next;
154             }
155            
156 2         7 my $uri_maps = {};
157            
158 2         12 foreach my $call (keys %$calls) {
159 10 50       76 unless ($obj->can($call)) {
160 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': Missing specified call ', $call, ' function');
161 0         0 undef($calls);
162 0         0 last;
163             }
164              
165 10         19 my $create_call = 0;
166              
167 10         19 foreach my $protocol_name (keys %{$self->{protocol}}) {
  10         35  
168 10         36 my $base = 'Lim::RPC::ProtocolCall::'.$protocol_name.'::'.ref($obj);
169              
170 10 50 33     238 if ($base->isa('UNIVERSAL') and $base->can($call)) {
171 0         0 next;
172             }
173 10         14 $create_call = 1;
174 10         24 last;
175             }
176              
177 10 50       33 if ($create_call) {
178 10         19 my $call_def = $calls->{$call};
179            
180 10 50       36 unless (ref($call_def) eq 'HASH') {
181 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid definition');
182 0         0 undef($calls);
183 0         0 last;
184             }
185            
186 10 100       34 if (exists $call_def->{uri_map}) {
187 6 50       42 unless (ref($call_def->{uri_map}) eq 'ARRAY') {
188 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid uri_map parameter definition');
189 0         0 undef($calls);
190 0         0 last;
191             }
192            
193 6         47 my $uri_map = Lim::RPC::URIMaps->new;
194            
195 6         15 foreach my $map (@{$call_def->{uri_map}}) {
  6         21  
196 10 50       45 if (defined (my $redirect_call = $uri_map->add($map))) {
197 10 100       41 if ($redirect_call) {
198 4 50       24 unless (exists $calls->{$redirect_call}) {
199 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid uri_map: redirected to non-existing call ', $redirect_call);
200 0         0 undef($calls);
201 0         0 last;
202             }
203             }
204             }
205             else {
206 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid uri_map: ', $@);
207 0         0 undef($calls);
208 0         0 last;
209             }
210             }
211 6 50       21 unless (defined $calls) {
212 0         0 last;
213             }
214            
215 6         20 $uri_maps->{$call} = $uri_map;
216             }
217            
218 10 100       33 if (exists $call_def->{in}) {
219 6 50       26 unless (ref($call_def->{in}) eq 'HASH') {
220 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid in parameter definition');
221 0         0 undef($calls);
222 0         0 last;
223             }
224            
225 6         11 my @keys = keys %{$call_def->{in}};
  6         27  
226 6 50       21 unless (scalar @keys) {
227 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid in parameter definition');
228 0         0 undef($calls);
229 0         0 last;
230             }
231            
232 6         15 my @values = ($call_def->{in});
233 6   66     63 while (defined $calls and (my $value = shift(@values))) {
234 12         32 foreach my $key (keys %$value) {
235 12 100       59 if (ref($value->{$key}) eq 'HASH') {
    50          
236 6 50       19 if (exists $value->{$key}->{''}) {
237 0         0 eval {
238 0         0 my $collection = Lim::RPC::Value::Collection->new($value->{$key}->{''});
239 0         0 delete $value->{$key}->{''};
240 0         0 $value->{$key} = $collection->set_children($value->{$key});
241             };
242 0 0       0 unless ($@) {
243 0         0 push(@values, $value->{$key}->children);
244 0         0 next;
245             }
246 0 0       0 Lim::WARN and $self->{logger}->warn('Unable to create Lim::RPC::Value::Collection: ', $@);
247             }
248             else {
249 6         13 push(@values, $value->{$key});
250 6         44 next;
251             }
252             }
253             elsif (blessed $value->{$key}) {
254 0 0       0 if ($value->{$key}->isa('Lim::RPC::Value')) {
255 0         0 next;
256             }
257 0 0       0 if ($value->{$key}->isa('Lim::RPC::Value::Collection')) {
258 0         0 push(@values, $value->{$key}->children);
259 0         0 next;
260             }
261             }
262             else {
263 6         9 eval {
264 6         96 $value->{$key} = Lim::RPC::Value->new($value->{$key});
265             };
266 6 50       19 unless ($@) {
267 6         49 next;
268             }
269 0 0       0 Lim::WARN and $self->{logger}->warn('Unable to create Lim::RPC::Value: ', $@);
270             }
271              
272 0 0       0 Lim::WARN and $self->{logger}->warn('Can not server ', $name, ': call ', $call, ' has invalid in parameter definition');
273 0         0 undef($calls);
274             }
275             }
276            
277 6 50       22 unless (defined $calls) {
278 0         0 last;
279             }
280             }
281            
282 10 50       30 if (exists $call_def->{out}) {
283 10 50       35 unless (ref($call_def->{out}) eq 'HASH') {
284 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid out parameter definition');
285 0         0 undef($calls);
286 0         0 last;
287             }
288            
289 10         15 my @keys = keys %{$call_def->{out}};
  10         42  
290 10 50       32 unless (scalar @keys) {
291 0 0       0 Lim::WARN and $self->{logger}->warn('Can not serve ', $name, ': call ', $call, ' has invalid out parameter definition');
292 0         0 undef($calls);
293 0         0 last;
294             }
295              
296 10         20 my @values = ($call_def->{out});
297 10   66     72 while (defined $calls and (my $value = shift(@values))) {
298 18         56 foreach my $key (keys %$value) {
299 38 100       318 if (ref($value->{$key}) eq 'HASH') {
    50          
300 8 50       26 if (exists $value->{$key}->{''}) {
301 0         0 eval {
302 0         0 my $collection = Lim::RPC::Value::Collection->new($value->{$key}->{''});
303 0         0 delete $value->{$key}->{''};
304 0         0 $value->{$key} = $collection->set_children($value->{$key});
305             };
306 0 0       0 unless ($@) {
307 0         0 push(@values, $value->{$key}->children);
308 0         0 next;
309             }
310 0 0       0 Lim::WARN and $self->{logger}->warn('Unable to create Lim::RPC::Value::Collection: ', $@);
311             }
312             else {
313 8         19 push(@values, $value->{$key});
314 8         56 next;
315             }
316             }
317             elsif (blessed $value->{$key}) {
318 0 0       0 if ($value->{$key}->isa('Lim::RPC::Value')) {
319 0         0 next;
320             }
321 0 0       0 if ($value->{$key}->isa('Lim::RPC::Value::Collection')) {
322 0         0 push(@values, $value->{$key}->children);
323 0         0 next;
324             }
325             }
326             else {
327 30         39 eval {
328 30         107 $value->{$key} = Lim::RPC::Value->new($value->{$key});
329             };
330 30 50       319 unless ($@) {
331 30         211 next;
332             }
333 0 0       0 Lim::WARN and $self->{logger}->warn('Unable to create Lim::RPC::Value: ', $@);
334             }
335              
336 0 0       0 Lim::WARN and $self->{logger}->warn('Can not server ', $name, ': call ', $call, ' has invalid out parameter definition');
337 0         0 undef($calls);
338             }
339             }
340            
341 10 50       34 unless (defined $calls) {
342 0         0 last;
343             }
344             }
345            
346 10         24 my $logger = $self->{logger};
347 10         29 weaken($logger);
348              
349 10         12 foreach my $protocol_name (keys %{$self->{protocol}}) {
  10         37  
350 10         33 my $base = 'Lim::RPC::ProtocolCall::'.$protocol_name.'::'.ref($obj);
351 10         26 my $protocol_call = $base.'::'.$call;
352 10         23 my $protocol = $self->{protocol}->{$protocol_name};
353 10         187 weaken($protocol);
354 10         17 my $weak_obj = $obj;
355 10         19 weaken($weak_obj);
356 10         22 weaken($call_def);
357              
358 10 50 33     224 if ($base->isa('UNIVERSAL') and $base->can($call)) {
359 0         0 next;
360             }
361            
362 7     7   89 no strict 'refs';
  7         20  
  7         3500  
363             *$protocol_call = sub {
364 2 50 33 2   41 unless (defined $protocol and defined $weak_obj and defined $call_def) {
      33        
365 0         0 return;
366             }
367 2         6 my ($self, $cb, $q, @args);
368 2         6 eval {
369 2         161 ($self, $cb, $q, @args) = $protocol->precall($call, @_);
370             };
371 2 50       11 if ($@) {
372 0 0 0     0 Lim::WARN and defined $logger and $logger->warn($weak_obj, '->', $call, '() precall failed: ', $@);
373 0         0 $weak_obj->Error($cb);
374 0         0 return;
375             }
376            
377 2 50 33     9 Lim::RPC_DEBUG and defined $logger and $logger->debug('Call to ', $weak_obj, ' ', $call);
378              
379 2 50       1000 if (!defined $q) {
380 2         7 $q = {};
381             }
382 2 50       12 if (ref($q) ne 'HASH') {
383 0 0 0     0 Lim::WARN and defined $logger and $logger->warn($weak_obj, '->', $call, '() called without data as hash');
384 0         0 $weak_obj->Error($cb);
385 0         0 return;
386             }
387            
388 2 50       17 if (exists $call_def->{in}) {
    50          
389 0         0 eval {
390 0         0 Lim::RPC::V($q, $call_def->{in});
391             };
392 0 0       0 if ($@) {
393 0 0 0     0 Lim::WARN and defined $logger and $logger->warn($weak_obj, '->', $call, '() data validation failed: ', $@);
394 0 0 0     0 Lim::DEBUG and defined $logger and eval {
395 7     7   8406 use Data::Dumper;
  7         51598  
  7         11554  
396 0         0 $logger->debug(Dumper($q));
397 0         0 $logger->debug(Dumper($call_def->{in}));
398             };
399 0         0 $weak_obj->Error($cb);
400 0         0 return;
401             }
402             }
403             elsif (%$q) {
404 0 0 0     0 Lim::WARN and defined $logger and $logger->warn($weak_obj, '->', $call, '() have data but no definition');
405 0         0 $weak_obj->Error($cb);
406 0         0 return;
407             }
408 2         17 $cb->set_call_def($call_def);
409            
410 2         5 eval {
411 2         17 $weak_obj->$call($cb, $q, @args);
412             };
413 2 50       15 if ($@) {
414 0 0 0     0 Lim::WARN and defined $logger and $logger->warn($weak_obj, '->', $call, '() failed: ', $@);
415 0         0 $weak_obj->Error($cb);
416             }
417 2         9 return;
418 10         251 };
419             }
420             }
421             }
422 2 50       12 unless ($calls) {
423 0         0 next;
424             }
425              
426 2 50       12 Lim::DEBUG and $self->{logger}->debug('serving ', $name);
427            
428 2         1095 $self->{module}->{$name} = {
429             name => $name,
430             module => $module,
431             obj => $obj,
432             calls => $calls,
433             uri_maps => $uri_maps
434             };
435              
436 2         7 foreach my $protocol (values %{$self->{protocol}}) {
  2         9  
437 2         101 $protocol->serve($module, $name);
438             }
439             }
440             }
441            
442 2         10 $self;
443             }
444              
445             =head2 have_module
446              
447             =cut
448              
449             sub have_module {
450 2     2 1 8 my ($self, $module) = @_;
451            
452 2 50       14 unless (exists $self->{module}->{$module}) {
453 0         0 return;
454             }
455            
456 2         109 return 1;
457             }
458              
459             =head2 have_module_call
460              
461             =cut
462              
463             sub have_module_call {
464 2     2 1 9 my ($self, $module, $call) = @_;
465              
466 2 50       13 unless (exists $self->{module}->{$module}) {
467 0         0 return;
468             }
469              
470 2 50       19 unless (exists $self->{module}->{$module}->{calls}->{$call}) {
471 0         0 return;
472             }
473            
474 2         13 return 1;
475             }
476              
477             =head2 module_obj
478              
479             =cut
480              
481             sub module_obj {
482 0     0 1 0 my ($self, $module) = @_;
483              
484 0 0       0 unless (exists $self->{module}->{$module}) {
485 0         0 return;
486             }
487              
488 0         0 return $self->{module}->{$module}->{obj};
489             }
490              
491             =head2 module_class
492              
493             =cut
494              
495             sub module_class {
496 0     0 1 0 my ($self, $module) = @_;
497              
498 0 0       0 unless (exists $self->{module}->{$module}) {
499 0         0 return;
500             }
501              
502 0         0 return $self->{module}->{$module}->{module};
503             }
504              
505             =head2 module_obj_by_protocol
506              
507             =cut
508              
509             sub module_obj_by_protocol {
510 2     2 1 7 my ($self, $module, $protocol) = @_;
511            
512 2 50       14 unless (exists $self->{module}->{$module}) {
513 0         0 return;
514             }
515              
516 2 50       13 unless (exists $self->{protocol}->{$protocol}) {
517 0         0 return;
518             }
519            
520 2         30 bless {}, 'Lim::RPC::ProtocolCall::'.$protocol.'::'.$self->{module}->{$module}->{module}.'::Server';
521             }
522              
523             =head2 process_module_call_uri_map
524              
525             =cut
526              
527             sub process_module_call_uri_map {
528 0     0 1 0 my ($self, $module, $call, $uri, $data) = @_;
529              
530 0 0       0 unless (exists $self->{module}->{$module}) {
531 0         0 return;
532             }
533            
534 0 0       0 unless (ref($data) eq 'HASH') {
535 0         0 return;
536             }
537              
538 0 0       0 unless (exists $self->{module}->{$module}->{uri_maps}->{$call}) {
539 0         0 return;
540             }
541              
542 0         0 return $self->{module}->{$module}->{uri_maps}->{$call}->process($uri, $data);
543             }
544              
545             =head2 transports
546              
547             =cut
548              
549             sub transports {
550 2     2 1 8940 @{$_[0]->{transports}};
  2         168  
551             }
552              
553             =head1 AUTHOR
554              
555             Jerry Lundström, C<< >>
556              
557             =head1 BUGS
558              
559             Please report any bugs or feature requests to L.
560              
561             =head1 SUPPORT
562              
563             You can find documentation for this module with the perldoc command.
564              
565             perldoc Lim
566              
567             You can also look for information at:
568              
569             =over 4
570              
571             =item * Lim issue tracker (report bugs here)
572              
573             L
574              
575             =back
576              
577             =head1 ACKNOWLEDGEMENTS
578              
579             =head1 LICENSE AND COPYRIGHT
580              
581             Copyright 2012-2013 Jerry Lundström.
582              
583             This program is free software; you can redistribute it and/or modify it
584             under the terms of either: the GNU General Public License as published
585             by the Free Software Foundation; or the Artistic License.
586              
587             See http://dev.perl.org/licenses/ for more information.
588              
589              
590             =cut
591              
592             1; # End of Lim::RPC::Server