/[cvs]/nfo/perl/libs/Data/Transfer/Sync/API.pm
ViewVC logotype

Diff of /nfo/perl/libs/Data/Transfer/Sync/API.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.4 by joko, Sun Feb 9 05:03:02 2003 UTC revision 1.10 by joko, Sat Jun 19 01:43:03 2004 UTC
# Line 1  Line 1 
1    ## -------------------------------------------------------------------------
2    ##
3  ##    $Id$  ##    $Id$
4  ##  ##
5  ##    Copyright (c) 2002  Andreas Motl <andreas.motl@ilo.de>  ##    Copyright (c) 2002  Andreas Motl <andreas.motl@ilo.de>
6  ##  ##
7  ##    See COPYRIGHT section in pod text below for usage and distribution rights.  ##    See COPYRIGHT section in associated pod text
8    ##    or below for usage and distribution rights.
9  ##  ##
10  ##    ----------------------------------------------------------------------------------------  ## -------------------------------------------------------------------------
11  ##    $Log$  ##    $Log$
12    ##    Revision 1.10  2004/06/19 01:43:03  joko
13    ##    code from _prepareOptions now split into _prepareMap and _mergeOptions
14    ##
15    ##    Revision 1.9  2003/05/13 08:16:44  joko
16    ##    minor update: modified header
17    ##
18    ##    Revision 1.8  2003/03/27 15:31:15  joko
19    ##    fixes to modules regarding new namespace(s) below Data::Mungle::*
20    ##
21    ##    Revision 1.7  2003/02/21 08:00:24  joko
22    ##    debugging
23    ##
24    ##    Revision 1.6  2003/02/14 12:58:47  joko
25    ##    + re-enabled the erase-mechanism
26    ##
27    ##    Revision 1.5  2003/02/11 05:26:04  joko
28    ##    + sub _tellWhatIWillDo
29    ##    + re-enabled "branch to execution path for special targets" mechanism
30    ##
31  ##    Revision 1.4  2003/02/09 05:03:02  joko  ##    Revision 1.4  2003/02/09 05:03:02  joko
32  ##    + minor fix regarding namespace of api versioning extension module  ##    + minor fix regarding namespace of api versioning extension module
33  ##  ##
# Line 38  use mixin::with qw( Data::Transfer::Sync Line 60  use mixin::with qw( Data::Transfer::Sync
60  use Data::Dumper;  use Data::Dumper;
61  use Hash::Merge qw( merge );  use Hash::Merge qw( merge );
62    
63  use Data::Compare::Struct qw( isEmpty );  use Data::Mungle::Compare::Struct qw( isEmpty );
64    
65  # get logger instance  # get logger instance
66  my $logger = Log::Dispatch::Config->instance;  my $logger = Log::Dispatch::Config->instance;
# Line 66  sub _loadVersionExtensions { Line 88  sub _loadVersionExtensions {
88  sub configure {  sub configure {
89    my $self = shift;    my $self = shift;
90    
 #print "YAI\n";  
 #print Dumper(@_);  
 #exit;  
   
91    $logger->debug( __PACKAGE__ . "->configure");    $logger->debug( __PACKAGE__ . "->configure");
92    
93    my @args = @_;    my @args = @_;
94    
 #print Dumper(@args);  
   
95    if (!isEmpty(\@args)) {    if (!isEmpty(\@args)) {
96      my %properties = @_;      my %properties = @_;
97      # merge args to properties      # merge args to properties
# Line 123  sub readArguments { Line 139  sub readArguments {
139          dbKey => $self->{args_raw}->{source},          dbKey => $self->{args_raw}->{source},
140          nodeType => $self->{args_raw}->{'source-type'},          nodeType => $self->{args_raw}->{'source-type'},
141          nodeName => $self->{args_raw}->{'source-node'},          nodeName => $self->{args_raw}->{'source-node'},
142            ident => $self->{args_raw}->{'source-ident'},
143        },        },
144        target => {        target => {
145          dbKey => $self->{args_raw}->{target},          dbKey => $self->{args_raw}->{target},
# Line 150  sub syncNodes { Line 167  sub syncNodes {
167    my $self = shift;    my $self = shift;
168    my $args = shift;    my $args = shift;
169    
170      #$logger->notice( "==========================   " . __PACKAGE__ . "->syncNodes   ==============");
171    $logger->debug( __PACKAGE__ . "->syncNodes: starting" );    $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
172    
173  #print Dumper($self);  #print Dumper($self);
# Line 158  sub syncNodes { Line 176  sub syncNodes {
176  #print Dumper($self->{options});  #print Dumper($self->{options});
177    $self->_prepareOptions();    $self->_prepareOptions();
178    
179  #print Dumper($self->{options});    # trace
180        #print Dumper($self->{options});
181        #exit;
182    
183    if (!$self->checkOptions()) {    if (!$self->checkOptions()) {
184      $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");      $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
# Line 170  sub syncNodes { Line 190  sub syncNodes {
190      return;      return;
191    }    }
192    
193      #print Dumper($args);
194    
195    # remember arguments through the whole processing    # remember arguments through the whole processing
196    $self->{args} = $args;    $self->{args} = $args;
197    
# Line 179  sub syncNodes { Line 201  sub syncNodes {
201    # hash to sum up results    # hash to sum up results
202    # TODO: re-implement! (sync-statistics???)    # TODO: re-implement! (sync-statistics???)
203    
204      #print Dumper($self->{options}->{process});
205    
206    # detect synchronization method to determine which optical symbol (directed arrow) to use    # detect synchronization method to determine which optical symbol (directed arrow) to use
207    my $mode = $self->{args}->{mode};   # V1    my $mode = $self->{args}->{mode};   # V1
208    $mode ||= $self->{options}->{process}->{mode};    # V2    $mode ||= $self->{options}->{process}->{mode};    # V2
# Line 199  sub syncNodes { Line 223  sub syncNodes {
223      $self->options2metadata();      $self->options2metadata();
224      $self->options2metadata_accessor();      $self->options2metadata_accessor();
225    
226      # branch to execution path for special targets
227      # detect for option 'handler' which could be a CODEref
228        if ($self->{options}->{handler} && ref $self->{options}->{handler} eq 'CODE') {
229          $logger->info( __PACKAGE__ . "->syncNodes: Running (special handler code - no generic sync!) on '$self->{options}->{target}->{dbKey}' with MODE $self->{options}->{process}->{mode}, NODE $self->{options}->{target}->{address}");
230          #print Dumper($self);
231          #exit;
232          # don't do this any more - it wasn't very nice somehow ...
233          #$self->{options}->{handler}->($self->{app}, $self->{options});
234          # .... now: better let the parent application scope handle this via callback
235            # not any more required for this: $self->{app} inside here (which isn't the app we mean here)
236            # required for this: getting the options out of here: establish some getter method! ($self->getOptions(...))
237            # so....
238            #$self->{__bridge}->{parent_ref}->
239          
240          # ahh okay, DesignPattern::Bridge moves closer to some Class::Inner!???
241            # so...
242            # similar like above - but it isn't very nice anyway ...  (no privateness, but: who cares?)
243            #print Dumper($self->{__bridge});
244            # just take the global application instance and
245            # throw it into the context of the mapping module - this is heavy!  ;-) (but again, who cares...)
246            # TODO: handle this more abstract *sometime*
247            #$self->{options}->{handler}->($self->{__bridge}->{parent}->{app}, $self->{options});
248            $self->{options}->{handler}->($self->{__bridge}->{parent}->{process}, $self->{options});
249          
250          return;
251        }
252    
253      # TODO: execution path branch V2!!!
254        # option1: wrap this via callback to parent scope (like current impl. mechanism)
255        # option2: branch directly from here (this needs refactoring of the sub handler)
256    
257    # tracing    # tracing
258      #print Dumper($self);      #print Dumper($self);
259      #exit;      #exit;
260    
261    $logger->info( __PACKAGE__ . "->syncNodes: source=$self->{meta}->{source}->{dbKey}/$self->{meta}->{source}->{nodeName} [$self->{meta}->{source}->{nodeType}] $direction_arrow target=$self->{meta}->{target}->{dbKey}/$self->{meta}->{target}->{nodeName} [$self->{meta}->{target}->{nodeType}]" );    # V1:
262      $logger->debug( __PACKAGE__ . "->syncNodes: source=$self->{meta}->{source}->{dbKey}/$self->{meta}->{source}->{nodeName} [$self->{meta}->{source}->{nodeType}] $direction_arrow target=$self->{meta}->{target}->{dbKey}/$self->{meta}->{target}->{nodeName} [$self->{meta}->{target}->{nodeType}]" );
263      
264      # V2:
265      my $what = "$self->{meta}->{source}->{dbKey}/$self->{meta}->{source}->{nodeName} [$self->{meta}->{source}->{nodeType}] $direction_arrow $self->{meta}->{target}->{dbKey}/$self->{meta}->{target}->{nodeName} [$self->{meta}->{target}->{nodeType}]";
266      #my $header = ("~.." x 7) . "  " . $what . "  " . ("~.." x 4);
267      #my $header = ("= " x 7) . "  " . $what . "  " . ("= " x 4);
268      #my $header = ("~=-_-=" x 3) . "  " . $what . "  " . ("~=-_-=" x 4);
269      my $header = ("_-=~=-" x 4) . "  " . $what . "  " . ("_-=~=-" x 4);
270      $logger->notice($header);
271    
272    
273    return if !$self->buildFieldmapping();    return if !$self->buildFieldmapping();
274    return if !$self->_touchNodeSet();    return if !$self->_touchNodeSet();
275    return if !$self->_prepare_sync();    return if !$self->_prepare_sync();
276      $self->_tellWhatIWillDo();
277    
278    # tracing    # tracing
279      #print Dumper($self);      #print Dumper($self);
# Line 223  sub syncNodes { Line 289  sub syncNodes {
289  }  }
290    
291    
292    my $c_string_default = '';
293    sub c_string {
294      my $value = shift;
295      $value ||= "[$c_string_default]";
296      return $value;
297    }
298    
299    sub _tellWhatIWillDo {
300      my $self = shift;
301    
302    
303    #return;
304      
305      # trace
306        #print Dumper($self->{meta});
307        #exit;
308      
309      $c_string_default = 'n/a';
310      my $source = c_string($self->{opt}->{'source'});
311      my $source_node = c_string($self->{opt}->{'source-node'});
312      my $source_type = c_string($self->{opt}->{'source-type'});
313      my $target = c_string($self->{opt}->{'target'});
314      my $target_node = c_string($self->{opt}->{'target-node'});
315      my $target_type = c_string($self->{opt}->{'target-type'});
316      
317      my $mapping_module = c_string($self->{opt}->{'mapping-module'});
318      my $mode = uc c_string($self->{opt}->{'mode'});
319    
320      #my $ql = "$mode INTO $source NODE $source_node TYPE $source_type SELECT NODE $target_node TYPE $target_type FROM $target USING MODULE $mapping_module;";
321      #$logger->notice( __PACKAGE__ . ": $ql" );
322      my $ql = <<EOT;
323    
324      FETCH DATA
325        FROM STORAGE $self->{meta}->{source}->{dbKey}
326        AT NODE $self->{meta}->{source}->{accessorName}.$self->{meta}->{source}->{nodeName}
327        USING IDENTIFIER $self->{meta}->{source}->{IdentProvider}->{method}.$self->{meta}->{source}->{IdentProvider}->{arg}
328      CONVERT DATA
329        CAST FROM $self->{meta}->{source}->{nodeType} TO $self->{meta}->{target}->{nodeType}
330        MAP ATTRIBUTES FROM @{$self->{meta}->{source}->{childnodes}} TO @{$self->{meta}->{target}->{childnodes}}
331      STORE DATA
332        TO STORAGE $self->{meta}->{target}->{dbKey}
333        AT NODE $self->{meta}->{target}->{accessorName}.$self->{meta}->{target}->{nodeName}
334        USING IDENTIFIER $self->{meta}->{target}->{IdentProvider}->{method}.$self->{meta}->{target}->{IdentProvider}->{arg}
335    EOT
336      
337      
338      chomp($ql);
339      $logger->info($ql);
340      
341      #exit;
342      return;
343      
344      my $actioning = ucfirst $self->{opt}->{'action'} . 'ing';
345      
346      # FIXME: this is weird!
347      my $long = <<EOT;
348    
349        - $actioning data of type $target_type and
350          filtered by $target_node from the storage named $target
351          to the storage named $source - filtered by $source_node.
352        - Will attempt to convert the data to $source_type.
353    EOT
354      chomp($long);
355      $logger->notice( __PACKAGE__ . ": $long" );
356      
357    }
358    
359    
360  sub _prepareOptions {  sub _prepareOptions {
361    
362    my $self = shift;    my $self = shift;
# Line 254  sub _prepareOptions { Line 388  sub _prepareOptions {
388    # inform user about option preparation    # inform user about option preparation
389    $logger->debug( __PACKAGE__ . "->_prepareOptions( source.node='$opts->{source}->{nodeName}', target.node='$opts->{target}->{nodeName}', mode='$opts->{process}->{mode}', e='$opts->{process}->{erase}', p='$opts->{process}->{prepare}' )");    $logger->debug( __PACKAGE__ . "->_prepareOptions( source.node='$opts->{source}->{nodeName}', target.node='$opts->{target}->{nodeName}', mode='$opts->{process}->{mode}', e='$opts->{process}->{erase}', p='$opts->{process}->{prepare}' )");
390    
391    # try to load mapping-metadata-container    my $map = {};
392      # How? Create a new instance of the given    $map = $self->_prepareMap($opts) if not $opts->{process}->{prepare};
393      # perl module/package name in ->{...}->{moduleName}.    #$map = $self->_prepareMap($opts);
394      # This instance is used later in the innards of the sync,  
395      # that's why the module (class) should have a certain layout    # merge all together
396      # enabling the core to use it for gathering metadata while processing.    $self->_mergeOptions($opts, $map);
397      my $mapObject = DesignPattern::Object->fromPackage($opts->{map}->{moduleName});    #print Dumper($self->{options});
398    
399    # try to resolve map from metadata-container    $self->{state}->{options_ready} = 1;
400    
401      # type of the item/node    return 1;
     my $source_nodeType = $opts->{source}->{nodeType};  
       
     # check if mapping for certain node is contained in mapping object  
     if (!$mapObject || !$mapObject->can($source_nodeType)) {  
       $logger->warning( __PACKAGE__ . "->_prepareOptions: Can't access mapping for source-type=\"$source_nodeType\" - please check \"$opts->{map}->{moduleName}\".");  
       return;  
     }  
402    
403      # get map  }
404      my $map = $mapObject->$source_nodeType;  
405      #print Dumper($map);  sub _prepareMap {
406      my $self = shift;
407      my $opts = shift;
408        
409      my $map_version = $map->{metadata}->{version};    # 1. try to load mapping-metadata-container
410      # FIXME: backward compatibility    
411      if (!$map_version) {    # How? Create a new instance of the given
412        $self->options_to_V2($map);    # perl module/package name in ->{...}->{moduleName}.
413      }    # This instance is used later in the innards of the sync,
414      # that's why the module (class) should have a certain layout
415      # enabling the core to use it for gathering metadata while processing.
416      my $mapObject = DesignPattern::Object->fromPackage($opts->{map}->{moduleName});
417    
     # trace  
       #print Dumper($map);  
       #exit;  
       #print "ref: ", ref $map->{target}, "\n";  
       #print "target: ", $map->{target}, "\n";  
       
     # check here if "target" is actually a CODEref - in this case: resolve it - deprecated!!! ???  
     if (ref $map->{target}->{address} eq 'CODE') {  
       $map->{target}->{address} = $map->{target}->{address}->($source_nodeType);  
     }  
       
     # resolve expressions (on nodename-level) here  
     elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {  
       my $target_dynamic_type = $1;  
       my $target_dynamic_expression = $2;  
       if (lc $target_dynamic_type eq 'code') {  
         $map->{target} = $mapObject->$target_dynamic_expression($map);  
       }  
     }  
418    
419    # merging - V1    # 2. try to resolve map from metadata-container
     #map { $opts->{$_} = $map->{$_}; } keys %$map;  
     # trace  
       #print Dumper($self->{options});  
     
   # merging - V2  
   
     # merge local map with local opts  
       # enable cloning  
       # FIXME: do we really need cloning here? trade safety/encapsulation for speed?  
       Hash::Merge::set_clone_behavior(1);  
       Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );  
       my $locals_merged = merge( $opts, $map );  
   
     # trace  
       #print Dumper($opts);  
       #print Dumper($map);  
       #print Dumper($locals_merged);  
       #exit;  
420    
421      # merge local-merged ones with instance-wide options    # type of the item/node
422        Hash::Merge::set_clone_behavior(0);    my $source_nodeType = $opts->{source}->{nodeType};
       $self->{options} = merge( $self->{options}, $locals_merged );  
423        
424      # trace    # check if mapping for certain node is contained in mapping object
425        #print Dumper($self->{options});    if (!$mapObject || !$mapObject->can($source_nodeType)) {
426        #exit;      $logger->warning( __PACKAGE__ . "->_prepareOptions: Can't access mapping for source-type=\"$source_nodeType\" - please check \"$opts->{map}->{moduleName}\".");
427        return;
428      }
429    
430      # get map
431      my $map = $mapObject->$source_nodeType;
432      #print Dumper($map);
433    
434      my $map_version = $map->{metadata}->{version};
435      # FIXME: backward compatibility
436      if (!$map_version) {
437        $self->options_to_V2($map);
438      }
439    
440      # trace
441        #print Dumper($map);
442        #exit;
443        #print "ref: ", ref $map->{target}, "\n";
444        #print "target: ", $map->{target}, "\n";
445      
446      # check here if "target" is actually a CODEref - in this case: resolve it - deprecated!!! ???
447      if (ref $map->{target}->{address} eq 'CODE') {
448        $map->{target}->{address} = $map->{target}->{address}->($source_nodeType);
449      }
450        
451      # resolve expressions (on nodename-level) here
452      elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
453        my $target_dynamic_type = $1;
454        my $target_dynamic_expression = $2;
455        if (lc $target_dynamic_type eq 'code') {
456          $map->{target} = $mapObject->$target_dynamic_expression($map);
457        }
458      }
459      
460      return $map;
461      
462    }
463    
464    $self->{state}->{options_ready} = 1;  sub _mergeOptions {
465      my $self = shift;
466      my $opts = shift;
467      my $map = shift;
468    
469    return 1;    # 1. merge local map with local opts
470      
471      # delete undef'd items in $map
472    
473      # enable cloning
474      # FIXME: do we really need cloning here? trade safety/encapsulation for speed?
475      Hash::Merge::set_clone_behavior(1);
476      Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );
477      #Hash::Merge::set_behavior( 'STORAGE_PRECEDENT' );
478      #Hash::Merge::set_behavior( 'RETAINMENT_PRECEDENT' );
479      # TODO: add an option to Hash::Merge not to overwrite set items with undefined/empty/not assigned ones
480      my $locals_merged = merge( $opts, $map );
481    
482      # 2. merge local-merged ones with instance-wide options
483      Hash::Merge::set_clone_behavior(0);
484      $self->{options} = merge( $self->{options}, $locals_merged );
485  }  }
486    
   
487  sub _preCheckOptions {  sub _preCheckOptions {
488    
489    my $self = shift;    my $self = shift;
# Line 348  sub _preCheckOptions { Line 493  sub _preCheckOptions {
493      #print Dumper($opts);      #print Dumper($opts);
494      #exit;      #exit;
495            
496    if (!$opts->{process}->{mode}) {    if (!$opts->{process}->{mode} and !$opts->{process}->{prepare}) {
497      $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"--action=(load|save)\".");      $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"--action=(load|save)\" or \"--prepare\".");
498      return;      return;
499    }    }
500    
# Line 369  sub _preCheckOptions { Line 514  sub _preCheckOptions {
514      $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");      $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");
515      return;      return;
516    }    }
517    if (!$opts->{map}->{moduleName}) {    if (!$opts->{map}->{moduleName} and !$opts->{process}->{prepare}) {
518      $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module=My::Mapping::Module'.");      $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module=My::Mapping::Module'.");
519      return;      return;
520    }    }
# Line 400  sub _prepare_sync { Line 545  sub _prepare_sync {
545      # TODO: introduce different mechanism to make more then two partners (descents) possible      # TODO: introduce different mechanism to make more then two partners (descents) possible
546      ($self->{meta}->{source}, $self->{meta}->{target}) =      ($self->{meta}->{source}, $self->{meta}->{target}) =
547          ($self->{meta}->{target}, $self->{meta}->{source});          ($self->{meta}->{target}, $self->{meta}->{source});
548        #($self->{options}->{source}, $self->{options}->{target}) =
549        #    ($self->{options}->{target}, $self->{options}->{source});
550        
551    } elsif (lc $self->{options}->{process}->{mode} eq 'full') {    } elsif (lc $self->{options}->{process}->{mode} eq 'full') {
552      # TODO:      # TODO:
# Line 413  sub _prepare_sync { Line 560  sub _prepare_sync {
560    
561    # import flag means: prepare the source node to be syncable    # import flag means: prepare the source node to be syncable
562    # this is useful if there are e.g. no "ident" or "checksum" columns yet inside a DBI like (row-based) storage    # this is useful if there are e.g. no "ident" or "checksum" columns yet inside a DBI like (row-based) storage
563    if ($self->{args}->{prepare}) {    if ($self->{options}->{process}->{prepare}) {
564      $self->_prepareNode_MetaProperties('source');      $self->_prepareNode_MetaProperties('source');
565      $self->_prepareNode_DummyIdent('source');      $self->_prepareNode_DummyIdent('source');
566      #return;      return;
567      #$self->_erase_all($opts->{source_node});      #$self->_erase_all($opts->{source_node});
568    }    }
569        
570    # erase flag means: erase the target    # erase flag means: erase the target
571    #if ($opts->{erase}) {    #if ($opts->{erase}) {
572    if ($self->{args}->{erase}) {    if ($self->{options}->{process}->{erase}) {
573      # TODO: move this method to the scope of the synchronization core and wrap it around different handlers      # TODO: move this method to the scope of the synchronization core and wrap it around different handlers
574      #print "ERASE", "\n";      #print "ERASE", "\n";
575      $self->_erase_all('target');      $self->_erase_all('target');

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.10

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed