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

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

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

revision 1.2 by joko, Sun Jan 19 02:05:42 2003 UTC revision 1.6 by joko, Fri Feb 14 14:03:49 2003 UTC
# Line 6  Line 6 
6  ##  ##
7  ##    ----------------------------------------------------------------------------------------  ##    ----------------------------------------------------------------------------------------
8  ##    $Log$  ##    $Log$
9    ##    Revision 1.6  2003/02/14 14:03:49  joko
10    ##    + logging, debugging
11    ##    - refactored code to sister module
12    ##
13    ##    Revision 1.5  2003/02/11 05:30:47  joko
14    ##    + minor fixes and some debugging mud
15    ##
16    ##    Revision 1.4  2003/02/09 05:01:10  joko
17    ##    + major structure changes
18    ##    - refactored code to sister modules
19    ##
20    ##    Revision 1.3  2003/01/20 17:01:14  joko
21    ##    + cosmetics and debugging
22    ##    + probably refactored code to new plugin-modules 'Metadata.pm' and/or 'StorageInterface.pm' (guess it was the last one...)
23    ##
24  ##    Revision 1.2  2003/01/19 02:05:42  joko  ##    Revision 1.2  2003/01/19 02:05:42  joko
25  ##    - removed pod-documentation: now in Data/Transfer/Sync.pod  ##    - removed pod-documentation: now in Data/Transfer/Sync.pod
26  ##  ##
# Line 72  use mixin::with qw( Data::Transfer::Sync Line 87  use mixin::with qw( Data::Transfer::Sync
87  use Data::Dumper;  use Data::Dumper;
88    
89  use misc::HashExt;  use misc::HashExt;
 use libp qw( md5_base64 );  
 use libdb qw( quotesql hash2Sql );  
 use Data::Transform::Deep qw( hash2object refexpr2perlref );  
90  use Data::Compare::Struct qw( getDifference isEmpty );  use Data::Compare::Struct qw( getDifference isEmpty );
91  use Data::Storage::Container;  use Data::Storage::Container;
92  use DesignPattern::Object;  use DesignPattern::Object;
93    use libdb qw( quotesql );
94    
95  # get logger instance  # get logger instance
96  my $logger = Log::Dispatch::Config->instance;  my $logger = Log::Dispatch::Config->instance;
# Line 108  sub _init { Line 121  sub _init {
121      $self->{container}->addStorage($_, $self->{storages}->{$_});      $self->{container}->addStorage($_, $self->{storages}->{$_});
122    }    }
123        
124      # trace
125        #print Dumper($self);
126        #exit;
127    
128    return 1;    return 1;
129        
130  }  }
131    
132  sub _initV1 {  sub _initV1 {
133    my $self = shift;    my $self = shift;
134      $logger->debug( __PACKAGE__ . "->_initV1" );
135      die("this should not be reached!");
136    # tag storages with id-authority and checksum-provider information    # tag storages with id-authority and checksum-provider information
137    # TODO: better store tag inside metadata to hold bits together!    # TODO: better store tag inside metadata to hold bits together!
138    map { $self->{container}->{storage}->{$_}->{isIdentAuthority} = 1 } @{$self->{id_authorities}};    map { $self->{container}->{storage}->{$_}->{isIdentAuthority} = 1 } @{$self->{id_authorities}};
# Line 122  sub _initV1 { Line 141  sub _initV1 {
141  }  }
142    
143    
 sub _preCheckOptions {  
   
   my $self = shift;  
   my $opts = shift;  
   
 #print Dumper($opts);  
 #exit;  
   
   # the type of the to-be-synced item  
   if (!$opts->{source}->{nodeType}) {  
     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-type\".");  
     return;  
   }  
   # the name of the (container-) node the items are listed in  
   if (!$opts->{source}->{nodeName}) {  
     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-node\".");  
     return;  
   }  
   
   # a "map"-declaration which module to use for mapping- and/or lookup-purposes  
   if (!$opts->{map}) {  
     $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");  
     return;  
   }  
   if (!$opts->{map}->{moduleName}) {  
     $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module'.");  
     return;  
   }  
   
   return 1;  
   
 }  
   
   
   
   
   
   
   
   
   
 sub _buildFieldmappingV1 {  
   my $self = shift;  
   
   # build mapping  
   # incoming: and Array of node map entries (Array or Hash) - e.g.  
   #   [ 'source:item_name' => 'target:class_val' ]  
   #   { source => 'event->startDateTime', target => 'begindate' }  
   foreach (@{$self->{args}->{mapping}}) {  
     if (ref $_ eq 'ARRAY') {  
       my @entry1 = split(':', $_->[0]);  
       my @entry2 = split(':', $_->[1]);  
       my $descent = [];  
       my $node = [];  
       $descent->[0] = $entry1[0];  
       $descent->[1] = $entry2[0];  
       $node->[0] = $entry1[1];  
       $node->[1] = $entry2[1];  
       push @{$self->{meta}->{$descent->[0]}->{childnodes}}, $node->[0];  
       push @{$self->{meta}->{$descent->[1]}->{childnodes}}, $node->[1];  
     } elsif (ref $_ eq 'HASH') {  
       foreach my $entry_key (keys %$_) {  
         my $entry_val = $_->{$entry_key};  
         push @{$self->{meta}->{$entry_key}->{childnodes}}, $entry_val;  
       }  
     }  
   
   }  
   
 }  
   
 sub _buildMetadataV1 {  
   my $self = shift;  
   
   # decompose identifiers for each partner  
   # TODO: refactor!!! take this list from already established/given metadata  
   foreach ('source', 'target') {  
       
     # get/set metadata for further processing  
   
     # Partner and Node (e.g.: "L:Country" or "R:countries.csv")  
     if (my $item = $self->{args}->{$_}) {  
       my @item = split(':', $item);  
       $self->{meta}->{$_}->{dbkey} = $item[0];  
       $self->{meta}->{$_}->{node} = $item[1];  
     }  
       
     # Filter  
     if (my $item_filter = $self->{args}->{$_ . '_filter'}) {  
       $self->{meta}->{$_}->{filter} = $item_filter;  
     }  
   
     # IdentProvider  
     if (my $item_ident = $self->{args}->{$_ . '_ident'}) {  
       my @item_ident = split(':', $item_ident);  
       $self->{meta}->{$_}->{IdentProvider} = { method => $item_ident[0], arg => $item_ident[1] };  
     }  
   
 #print Dumper($self->{meta});  
   
     # TODO: ChecksumProvider  
   
     # exclude properties/subnodes  
     if (my $item_exclude = $self->{args}->{$_ . '_exclude'}) {  
       $self->{meta}->{$_}->{subnodes_exclude} = $item_exclude;  
     }  
       
     # TypeProvider  
     if (my $item_type = $self->{args}->{$_ . '_type'}) {  
       my @item_type = split(':', $item_type);  
       $self->{meta}->{$_}->{TypeProvider} = { method => $item_type[0], arg => $item_type[1] };  
     }  
       
     # Callbacks - writers (will be triggered _before_ writing to target)  
     if (my $item_writers = $self->{args}->{$_ . '_callbacks_write'}) {  
       my $descent = $_;     # this is important since the following code inside the map wants to use its own context variables  
       map { $self->{meta}->{$descent}->{Callback}->{write}->{$_}++; } @$item_writers;  
     }  
       
     # Callbacks - readers (will be triggered _after_ reading from source)  
     if (my $item_readers = $self->{args}->{$_ . '_callbacks_read'}) {  
       my $descent = $_;  
       map { $self->{meta}->{$descent}->{Callback}->{read}->{$_}++; } @$item_readers;  
     }  
       
     # resolve storage objects  
     #$self->{$_} = $self->{container}->{storage}->{$self->{meta}->{$_}->{dbkey}};  
     # relink references to metainfo  
     $self->{meta}->{$_}->{storage} = $self->{container}->{storage}->{$self->{meta}->{$_}->{dbkey}};  
     #print "iiiiisprov: ", Dumper($self->{meta}->{$_}->{storage}), "\n";  
   }  
   
 }  
   
144  # TODO: abstract the hardwired use of "source" and "target" in here somehow - hmmmm.......  /(="§/%???  # TODO: abstract the hardwired use of "source" and "target" in here somehow - hmmmm.......  /(="§/%???
145  sub _syncNodes {  sub _run {
146    
147    my $self = shift;    my $self = shift;
148        
149      #print "verbose: ", $self->{verbose}, "\n";
150      $self->{verbose} = 1;
151      
152      $logger->debug( __PACKAGE__ . "->_run" );
153    
154      # for statistics
155    my $tc = OneLineDumpHash->new( {} );    my $tc = OneLineDumpHash->new( {} );
156    my $results;    my $results;
157        
# Line 276  sub _syncNodes { Line 167  sub _syncNodes {
167    }    }
168        
169    # get reference to node list from convenient method provided by CORE-HANDLE    # get reference to node list from convenient method provided by CORE-HANDLE
   #$results ||= $self->{source}->getListUnfiltered($self->{meta}->{source}->{node});  
   #$results ||= $self->{meta}->{source}->{storage}->getListUnfiltered($self->{meta}->{source}->{node});  
170    $results ||= $self->_getNodeList('source');    $results ||= $self->_getNodeList('source');
171    
172    # checkpoint: do we actually have a list to iterate through?    # checkpoint: do we actually have a list to iterate through?
173    if (!$results || !@{$results}) {    if (!$results || !@{$results}) {
174      $logger->notice( __PACKAGE__ . "->syncNodes: No nodes to synchronize." );      $logger->notice( __PACKAGE__ . "->_run: No nodes to synchronize." );
175        return;
176      }
177    
178    
179    
180      # check if we actually *have* a synchronization method
181      if (!$self->{options}->{metadata}->{syncMethod}) {
182        $logger->critical( __PACKAGE__ . "->_run: No synchronization method (checksum|timestamp) specified" );
183      return;      return;
184    }    }
185        
186        
187    # dereference    # dereference
188    my @results = @{$results};    my @results = @{$results};
# Line 292  sub _syncNodes { Line 190  sub _syncNodes {
190    # iterate through set    # iterate through set
191    foreach my $source_node_real (@results) {    foreach my $source_node_real (@results) {
192    
193        print ":" if $self->{verbose};
194    
195      $tc->{total}++;      $tc->{total}++;
196    
197  #print "========================  iter", "\n";  #print "========================  iter", "\n";
# Line 301  sub _syncNodes { Line 201  sub _syncNodes {
201      #   - is a "deep_copy" needed here if occouring modifications take place?      #   - is a "deep_copy" needed here if occouring modifications take place?
202      #   - puuhhhh, i guess a deep_copy would destroy tangram mechanisms?      #   - puuhhhh, i guess a deep_copy would destroy tangram mechanisms?
203      #   - after all, just take care for now that this object doesn't get updated!      #   - after all, just take care for now that this object doesn't get updated!
204        #   - so, just use its reference for now - if some cloning is needed in future, do this here!
205      my $source_node = $source_node_real;      my $source_node = $source_node_real;
206    
207      # modify entry - handle new style callbacks (the readers)      # modify entry - handle new style callbacks (the readers)
208  #print Dumper($source_node);  
209  #exit;      # trace
210          #print Dumper($source_node);
211          #exit;
212    
213      my $descent = 'source';      my $descent = 'source';
214    
# Line 313  sub _syncNodes { Line 216  sub _syncNodes {
216      my $map_callbacks = {};      my $map_callbacks = {};
217      if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {      if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {
218    
219          # trace
220            #print Dumper($callbacks);
221            #exit;
222    
223        my $error = 0;        my $error = 0;
224    
225        foreach my $node (keys %{$callbacks->{read}}) {        foreach my $node (keys %{$callbacks->{read}}) {
# Line 320  sub _syncNodes { Line 227  sub _syncNodes {
227          my $object = $source_node;          my $object = $source_node;
228          my $value; # = $source_node->{$node};          my $value; # = $source_node->{$node};
229    
230          # trace
231            #print Dumper($self->{options});
232    
233          # ------------  half-redundant: make $self->callCallback($object, $value, $opts)          # ------------  half-redundant: make $self->callCallback($object, $value, $opts)
234          my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_read';          #my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_read';
235            my $perl_callback = $self->{meta}->{$descent}->{nodeType} . '::' . $node . '_read';
236          my $evalstring = 'return ' . $perl_callback . '( { object => $object, property => $node, value => $value, storage => $self->{meta}->{$descent}->{storage} } );';          my $evalstring = 'return ' . $perl_callback . '( { object => $object, property => $node, value => $value, storage => $self->{meta}->{$descent}->{storage} } );';
237          #print $evalstring, "\n"; exit;          #print $evalstring, "\n"; exit;
238          my $cb_result = eval($evalstring);          my $cb_result = eval($evalstring);
239          if ($@) {          if ($@) {
           die $@;  
240            $error = 1;            $error = 1;
241            print $@, "\n";            $logger->error( __PACKAGE__ . "->_run: $@" );
242              next;
243          }          }
244          # ------------  half-redundant: make $self->callCallback($object, $value, $opts)          # ------------  half-redundant: make $self->callCallback($object, $value, $opts)
245                    
# Line 338  sub _syncNodes { Line 249  sub _syncNodes {
249    
250      }      }
251    
252  #print Dumper($source_node);      # trace
253          #print Dumper($source_node);
254    
255      # exclude defined fields  (simply delete from object)      # exclude defined fields  (simply delete from object)
256      map { delete $source_node->{$_} } @{$self->{meta}->{source}->{subnodes_exclude}};      map { delete $source_node->{$_} } @{$self->{meta}->{source}->{subnodes_exclude}};
# Line 347  sub _syncNodes { Line 259  sub _syncNodes {
259      $self->{node} = {};      $self->{node} = {};
260      $self->{node}->{source}->{payload} = $source_node;      $self->{node}->{source}->{payload} = $source_node;
261    
262  #print "res - ident", "\n";      # trace
263          #print Dumper($self->{node});
264          #exit;
265    
266      # determine ident of entry      # determine ident of entry
267      my $identOK = $self->_resolveNodeIdent('source');      my $identOK = $self->_resolveNodeIdent('source');
268      #if (!$identOK && lc $self->{args}->{direction} ne 'import') {      #if (!$identOK && lc $self->{args}->{direction} ne 'import') {
269      if (!$identOK) {      if (!$identOK) {
270        #print Dumper($self->{meta}->{source});        #print Dumper($self->{meta}->{source});
271        $logger->critical( __PACKAGE__ . "->syncNodes: No ident found in source node \"$self->{meta}->{source}->{node}\", try to \"prepare\" this node first?" );        $logger->critical( __PACKAGE__ . "->_run: No ident found in source node ( nodeName='$self->{meta}->{source}->{nodeName}', nodeType='$self->{meta}->{source}->{nodeType}') try to \"prepare\" this node first?" );
272        return;        return;
273      }      }
274    
275  #print "statload", "\n";      #print "l" if $self->{verbose};
 #print "ident: ", $self->{node}->{source}->{ident}, "\n";  
 #print Dumper($self->{node});  
       
276      my $statOK = $self->_statloadNode('target', $self->{node}->{source}->{ident});      my $statOK = $self->_statloadNode('target', $self->{node}->{source}->{ident});
277    
 #print Dumper($self->{node});  
       
278      # mark node as new either if there's no ident or if stat/load failed      # mark node as new either if there's no ident or if stat/load failed
279      if (!$statOK) {      if (!$statOK) {
280        $self->{node}->{status}->{new} = 1;        $self->{node}->{status}->{new} = 1;
# Line 374  sub _syncNodes { Line 283  sub _syncNodes {
283    
284  #print "checksum", "\n";  #print "checksum", "\n";
285            
286        #print Dumper($self);
287        
288      # determine status of entry by synchronization method      # determine status of entry by synchronization method
289      if ( (lc $self->{args}->{method} eq 'checksum') ) {      if ( lc $self->{options}->{metadata}->{syncMethod} eq 'checksum' ) {
290      #if ( $statOK && (lc $self->{args}->{method} eq 'checksum') ) {      #if ( $statOK && (lc $self->{args}->{method} eq 'checksum') ) {
291      #if ( !$self->{node}->{status}->{new} && (lc $self->{args}->{method} eq 'checksum') ) {      #if ( !$self->{node}->{status}->{new} && (lc $self->{args}->{method} eq 'checksum') ) {
292                
293        # TODO:      # calculate checksum of source node
       # is this really worth a "critical"???  
       # no - it should just be a debug appendix i believe  
   
 #print "readcs", "\n";  
         
       # calculate checksum of source node  
294        #$self->_calcChecksum('source');        #$self->_calcChecksum('source');
295        if (!$self->_readChecksum('source')) {        if (!$self->_readChecksum('source')) {
296          $logger->critical( __PACKAGE__ . "->_readChecksum: Could not find \"source\" entry with ident=\"$self->{node}->{source}->{ident}\"" );          $logger->warning( __PACKAGE__ . "->_run: Could not find \"source\" entry with ident=\"$self->{node}->{source}->{ident}\"" );
297          $tc->{skip}++;          $tc->{skip}++;
298          print "s" if $self->{verbose};          print "s" if $self->{verbose};
299          next;          next;
# Line 410  sub _syncNodes { Line 315  sub _syncNodes {
315        # determine if entry is "new" or "dirty"        # determine if entry is "new" or "dirty"
316        # after all, this seems to be the point where the hammer falls.....        # after all, this seems to be the point where the hammer falls.....
317        print "c" if $self->{verbose};        print "c" if $self->{verbose};
318    
319          # trace
320            #print Dumper($self->{node});
321            #exit;
322    
323        $self->{node}->{status}->{new} = !$self->{node}->{target}->{checksum};        $self->{node}->{status}->{new} = !$self->{node}->{target}->{checksum};
324        if (!$self->{node}->{status}->{new}) {        if (!$self->{node}->{status}->{new}) {
325          $self->{node}->{status}->{dirty} =          $self->{node}->{status}->{dirty} =
# Line 419  sub _syncNodes { Line 329  sub _syncNodes {
329            $self->{args}->{force};            $self->{args}->{force};
330        }        }
331    
332        } else {
333          $logger->warning( __PACKAGE__ . "->_run: Synchronization method '$self->{options}->{metadata}->{syncMethod}' is not implemented" );
334          $tc->{skip}++;
335          print "s" if $self->{verbose};
336          next;
337          
338      }      }
339    
340      # first reaction on entry-status: continue with next entry if the current is already "in sync"      # first reaction on entry-status: continue with next entry if the current is already "in sync"
# Line 428  sub _syncNodes { Line 344  sub _syncNodes {
344      }      }
345    
346      # build map to actually transfer the data from source to target      # build map to actually transfer the data from source to target
347      $self->_buildMap();      $self->buildAttributeMap();
   
348    
 #print Dumper($self->{node}); exit;  
349    
350  #print "attempt", "\n";      # trace
351          #print Dumper($self->{node}); exit;
352          #print "attempt", "\n";
353    
354      # additional (new) checks for feature "write-protection"      # additional (new) checks for feature "write-protection"
355      if ($self->{meta}->{target}->{storage}->{isWriteProtected}) {      if ($self->{meta}->{target}->{storage}->{isWriteProtected}) {
356        $tc->{attempt_transfer}++;        $tc->{attempt_transfer}++;
357        print "\n" if $self->{verbose};        print "\n" if $self->{verbose};
358        $logger->notice( __PACKAGE__ . "->syncNodes: Target is write-protected. Will not insert or modify node. " .        $logger->notice( __PACKAGE__ . "->_run: Target is write-protected. Will not insert or modify node. " .
359            "(Ident: $self->{node}->{source}->{ident} " . "Dump:\n" . Dumper($self->{node}->{source}->{payload}) . ")" );            "(Ident: $self->{node}->{source}->{ident} " . "Dump:\n" . Dumper($self->{node}->{source}->{payload}) . ")" );
360        print "\n" if $self->{verbose};        print "\n" if $self->{verbose};
361        $tc->{skip}++;        $tc->{skip}++;
362        next;        next;
363      }      }
364    
365        # trace
366          #print Dumper($self);
367          #exit;
368    
369      # transfer contents of map to target      # transfer contents of map to target
370      if ($self->{node}->{status}->{new}) {      if ($self->{node}->{status}->{new}) {
371        $tc->{attempt_new}++;        $tc->{attempt_new}++;
# Line 474  sub _syncNodes { Line 394  sub _syncNodes {
394        print "e" if $self->{verbose};        print "e" if $self->{verbose};
395      }      }
396            
397        # trace
398          #print Dumper($self);
399          #exit;
400        
401      # change ident in source (take from target), if transfer was ok and target is an IdentAuthority      # change ident in source (take from target), if transfer was ok and target is an IdentAuthority
402      # this is (for now) called a "retransmit" indicated by a "r"-character when verbosing      # this is (for now) called a "retransmit" indicated by a "r"-character when verbosing
403      if ($self->{node}->{status}->{ok} && $self->{meta}->{target}->{storage}->{isIdentAuthority}) {      #if ($self->{node}->{status}->{ok} && $self->{options}->{target}->{storage}->{idAuthority}) {
404        if ($self->{node}->{status}->{ok} && $self->{meta}->{target}->{isIdentAuthority}) {
405        print "r" if $self->{verbose};        print "r" if $self->{verbose};
406        #print Dumper($self->{meta});        #print Dumper($self->{meta});
407        #print Dumper($self->{node});        #print Dumper($self->{node});
# Line 484  sub _syncNodes { Line 409  sub _syncNodes {
409        $self->_doModifySource_IdentChecksum($self->{node}->{target}->{ident});        $self->_doModifySource_IdentChecksum($self->{node}->{target}->{ident});
410      }      }
411    
     print ":" if $self->{verbose};  
   
412    }    }
413    
414    print "\n" if $self->{verbose};    print "\n" if $self->{verbose};
# Line 501  sub _syncNodes { Line 424  sub _syncNodes {
424            
425      # todo!!!      # todo!!!
426      #sysevent( { usermsg => $msg, level => $level }, $taskEvent );      #sysevent( { usermsg => $msg, level => $level }, $taskEvent );
427      $logger->info( __PACKAGE__ . "->syncNodes: $msg" );      $logger->info( __PACKAGE__ . "->_run: $msg" );
428    
429    return $tc;    return $tc;
430    
# Line 552  sub _dumpCompact { Line 475  sub _dumpCompact {
475  }  }
476    
477    
 sub _calcChecksum {  
   
   my $self = shift;  
   my $descent = shift;  
   my $specifier = shift;  
   
   # calculate checksum for current object  
     my $ident = $self->{node}->{$descent}->{ident};  
     
   # build dump of this node  
     my $payload = $self->{node}->{$descent}->{payload};  
     #my $dump = $ident . "\n" . $item->quickdump();  
     #my $dump = $ident . "\n" . Dumper($item);  
     my $dump = $ident . "\n" . $self->_dumpCompact($payload);  
     
   # TODO: $logger->dump( ... );  
     #$logger->debug( __PACKAGE__ . ": " . $dump );  
     #$logger->dump( __PACKAGE__ . ": " . $dump );  
     
   # calculate checksum from dump  
     # note: the 32-bit integer hash from DBI seems  
     # to generate duplicates with small payloads already in ranges of hundreds of items/rows!!!  
     # try to avoid to use it or try to use it only for payloads greater than, hmmm, let's say 30 chars?  
     # (we had about 15 chars average per item (row))  
   
     # md5-based fingerprint, base64 encoded (from Digest::MD5)  
       $self->{node}->{$descent}->{checksum} = md5_base64($dump) . '==';  
     # 32-bit integer "hash" value (maybe faster?) (from DBI)  
       #$self->{node}->{$descent}->{checksum} = DBI::hash($dump, 1);  
   
   # signal good  
   return 1;  
   
 }  
   
   
 sub _readChecksum {  
   my $self = shift;  
   
   my $descent = shift;  
   
   #print "getcheck:", "\n"; print Dumper($self->{node}->{$descent});  
     
   if (!$self->{node}->{$descent}) {  
     # signal checksum bad  
     return;  
   }  
   
   # get checksum for current entry  
   # TODO: don't have the checksum column/property hardcoded as "cs" here, make this configurable somehow  
   
   if ($self->{meta}->{$descent}->{storage}->{isChecksumAuthority}) {  
     #$self->{node}->{$descent}->{checksum} = $entry->{cs};  
     #$self->{node}->{$descent}->{checksum} = $self->_calcChecksum($descent); # $entry->{cs};  
     #print "descent: $descent", "\n";  
     $self->_calcChecksum($descent);  
     #print "checksum: ", $self->{node}->{$descent}->{checksum}, "\n";  
   } else {  
   
     #$self->{node}->{$descent}->{checksum} = $entry->{cs};  
     $self->{node}->{$descent}->{checksum} = $self->{node}->{$descent}->{payload}->{cs};  
   }  
   
   # signal checksum good  
   return 1;  
   
 }  
   
   
 sub _buildMap {  
   
  my $self = shift;  
   
   # field-structure for building sql  
   # mapping of sql-fieldnames to object-attributes  
     $self->{node}->{map} = {};  
   
     # manually set ...  
       # ... object-id  
       $self->{node}->{map}->{$self->{meta}->{target}->{IdentProvider}->{arg}} = $self->{node}->{source}->{ident};  
       # ... checksum  
       $self->{node}->{map}->{cs} = $self->{node}->{source}->{checksum};  
   
 #print "sqlmap: ", Dumper($self->{node}->{map}), "\n";  
   
     # for transferring flat structures via simple (1:1) mapping  
     # TODO: diff per property / property value  
   
     if ($self->{args}->{mapping}) {  
       # apply mapping from $self->{args}->{mapping} to $self->{node}->{map}  
       #foreach my $key (@{$self->{meta}->{source}->{childnodes}}) {  
       my @childnodes = @{$self->{meta}->{source}->{childnodes}};  
       for (my $mapidx = 0; $mapidx <= $#childnodes; $mapidx++) {  
         #my $map_right = $self->{args}->{mapping}->{$key};  
           
         $self->{node}->{source}->{propcache} = {};  
         $self->{node}->{target}->{propcache} = {};  
           
         # get property name  
         $self->{node}->{source}->{propcache}->{property} = $self->{meta}->{source}->{childnodes}->[$mapidx];  
         $self->{node}->{target}->{propcache}->{property} = $self->{meta}->{target}->{childnodes}->[$mapidx];  
         #print "map: $map_right", "\n";  
   
         # get property value  
         my $value;  
           
         # detect for callback - old style - (maybe the better???)  
         if (ref($self->{node}->{target}->{map}) eq 'CODE') {  
           #$value = &$map_right($objClone);  
         } else {  
           # plain (scalar?) value  
           #$value = $objClone->{$map_right};  
           $self->{node}->{source}->{propcache}->{value} = $self->{node}->{source}->{payload}->{$self->{node}->{source}->{propcache}->{property}};  
         }  
         #$self->{node}->{map}->{$key} = $value;  
           
         # detect expression  
         # for transferring deeply nested structures described by expressions  
         #print "val: $self->{node}->{source}->{propcache}->{value}", "\n";  
         if ($self->{node}->{source}->{propcache}->{property} =~ s/^expr://) {  
             
           # create an anonymous sub to act as callback target dispatcher  
             my $cb_dispatcher = sub {  
               #print "===============  CALLBACK DISPATCHER", "\n";  
               #print "ident: ", $self->{node}->{source}->{ident}, "\n";  
               #return $self->{node}->{source}->{ident};  
                 
             };  
             
   
 #print Dumper($self->{node});  
             
           # build callback map for helper function  
           #my $cbmap = { $self->{meta}->{source}->{IdentProvider}->{arg} => $cb_dispatcher };  
           my $cbmap = {};  
           my $value = refexpr2perlref($self->{node}->{source}->{payload}, $self->{node}->{source}->{propcache}->{property}, $cbmap);  
           $self->{node}->{source}->{propcache}->{value} = $value;  
         }  
   
         # encode values dependent on type of underlying storage here - expand cases...  
         my $storage_type = $self->{meta}->{target}->{storage}->{locator}->{type};  
         if ($storage_type eq 'DBI') {  
           # ...for sql  
           $self->{node}->{source}->{propcache}->{value} = quotesql($self->{node}->{source}->{propcache}->{value});  
         }  
          elsif ($storage_type eq 'Tangram') {  
           # iso? utf8 already possible?  
           
         } elsif ($storage_type eq 'LDAP') {  
           # TODO: encode utf8 here?  
         }  
   
         # store value to transfer map  
         $self->{node}->{map}->{$self->{node}->{target}->{propcache}->{property}} = $self->{node}->{source}->{propcache}->{value};  
   
       }  
     }  
   
       
   # TODO: $logger->dump( ... );  
   #$logger->debug( "sqlmap:" . "\n" . Dumper($self->{node}->{map}) );  
 #print "sqlmap: ", Dumper($self->{node}->{map}), "\n";  
 #print "entrystatus: ", Dumper($self->{node}), "\n";  
   
 }  
   
 sub _resolveNodeIdent {  
   my $self = shift;  
   my $descent = shift;  
     
   #print Dumper($self->{node}->{$descent});  
     
   # get to the payload  
     #my $item = $specifier->{item};  
     my $payload = $self->{node}->{$descent}->{payload};  
   
   # resolve method to get to the id of the given item  
   # we use global metadata and the given descent for this task  
     #my $ident = $self->{$descent}->id($item);  
     #my $ident = $self->{meta}->{$descent}->{storage}->id($item);  
   
     my $ident;  
     my $provider_method = $self->{meta}->{$descent}->{IdentProvider}->{method};  
     my $provider_arg = $self->{meta}->{$descent}->{IdentProvider}->{arg};  
   
     # resolve to ident  
     if ($provider_method eq 'property') {  
       $ident = $payload->{$provider_arg};  
   
     } elsif ($provider_method eq 'storage_method') {  
       #$ident = $self->{meta}->{$descent}->{storage}->id($item);  
       $ident = $self->{meta}->{$descent}->{storage}->$provider_arg($payload);  
     }  
       
     $self->{node}->{$descent}->{ident} = $ident;  
       
   return 1 if $ident;  
   
 }  
   
478    
479  sub _modifyNode {  sub _doTransferToTarget {
480    my $self = shift;    my $self = shift;
   my $descent = shift;  
481    my $action = shift;    my $action = shift;
   my $map = shift;  
   my $crit = shift;  
   
   # map for new style callbacks  
   my $map_callbacks = {};  
   
   # checks go first!  
     
     # TODO: this should be reviewed first - before extending  ;-)  
     # TODO: this should be extended:  
     # count this cases inside the caller to this sub and provide a better overall message  
     # if this counts still zero in the end:  
     #     "No nodes have been touched for modify: Do you have column-headers in your csv file?"  
     if (not defined $self->{node}) {  
       #$logger->critical( __PACKAGE__ . "->_modifyNode failed: \"$descent\" node is empty." );  
       #return;  
     }  
   
   # transfer callback nodes from value map to callback map - handle them afterwards! - (new style callbacks)  
   if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {  
     foreach my $callback (keys %{$callbacks->{write}}) {  
       $map_callbacks->{write}->{$callback} = $map->{$callback};  
       delete $map->{$callback};  
     }  
   }  
     
     
   #print Dumper($self->{meta});  
   
   # DBI speaks SQL  
   if ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'DBI') {  
   
 #print Dumper($self->{node});  
     my $sql_main;  
     # translate map to sql  
     #print $action, "\n"; exit;  
     #print $self->{meta}->{$descent}->{node}, "\n"; exit;  
     #print "action:";  
     #print $action, "\n";  
 #$action = "anc";  
 #print "yai", "\n";  
   
 #print Dumper($map);  
 #delete $map->{cs};  
   
     if (lc($action) eq 'insert') {  
       $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_INSERT');  
     } elsif (lc $action eq 'update') {  
       $crit ||= "$self->{meta}->{$descent}->{IdentProvider}->{arg}='$self->{node}->{$descent}->{ident}'";  
       $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_UPDATE', $crit);  
     }  
   
 #$sql_main = "UPDATE currencies_csv SET oid='abcdef' WHERE text='Australian Dollar' AND key='AUD';";  
 #$sql_main = "UPDATE currencies_csv SET oid='huhu2' WHERE ekey='AUD'";  
   
 #print "sql: ", $sql_main, "\n";  
 #exit;  
   
     # transfer data  
     my $sqlHandle = $self->{meta}->{$descent}->{storage}->sendCommand($sql_main);  
   
 #exit;  
   
     # handle errors  
     if ($sqlHandle->err) {  
       #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }  
       $self->{node}->{status}->{error} = {  
         statement => $sql_main,  
         state => $sqlHandle->state,  
         err => $sqlHandle->err,  
         errstr => $sqlHandle->errstr,  
       };  
     } else {  
       $self->{node}->{status}->{ok} = 1;  
     }  
   
   # Tangram does it the oo-way (naturally)  
   } elsif ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'Tangram') {  
     my $sql_main;  
     my $object;  
   
     # determine classname  
     my $classname = $self->{meta}->{$descent}->{node};  
       
     # properties to exclude  
     my @exclude = @{$self->{meta}->{$descent}->{subnodes_exclude}};  
   
   
     if (my $identProvider = $self->{meta}->{$descent}->{IdentProvider}) {  
       push @exclude, $identProvider->{arg};  
     }  
   
     # new feature:  
     #     - check TypeProvider metadata property from other side  
     #     - use argument (arg) inside as a classname for object creation on this side  
     #my $otherSide = $self->_otherSide($descent);  
     if (my $typeProvider = $self->{meta}->{$descent}->{TypeProvider}) {  
       #print Dumper($map);  
       $classname = $map->{$typeProvider->{arg}};  
       # remove nodes from map also (push nodes to "subnodes_exclude" list)  
       push @exclude, $typeProvider->{arg};  
     }  
       
     # exclude banned properties (remove from map)  
     #map { delete $self->{node}->{map}->{$_} } @{$self->{args}->{exclude}};  
     map { delete $map->{$_} } @exclude;  
   
     # list of properties  
     my @props = keys %{$map};  
       
     # transfer data  
     if (lc $action eq 'insert') {  
   
       # build array to initialize object  
       #my @initarray = ();  
       #map { push @initarray, $_, undef; } @props;  
   
       # make the object persistent in four steps:  
       #   - raw create (perl / class tangram scope)  
       #   - engine insert (tangram scope)   ... this establishes inheritance - don't try to fill in inherited properties before!  
       #   - raw fill-in from hash (perl scope)  
       #   - engine update (tangram scope)  ... this updates all properties just filled in  
         
       # create new object ...  
       #my $object = $classname->new( @initarray );  
       $object = $classname->new();  
         
       # ... pass to orm ...  
       $self->{meta}->{$descent}->{storage}->insert($object);  
   
       # ... and initialize with empty (undef'd) properties.  
       #print Dumper(@props);  
       map { $object->{$_} = undef; } @props;  
   
       # mix in values ...  
       hash2object($object, $map);  
   
       # ... and re-update@orm.  
 #print Dumper($object);  
       $self->{meta}->{$descent}->{storage}->update($object);  
   
       # asymmetry: get ident after insert  
       # TODO:  
       #   - just do this if it is an IdentAuthority  
       #   - use IdentProvider metadata here  
 #print Dumper($self->{meta}->{$descent});  
       my $oid = $self->{meta}->{$descent}->{storage}->id($object);  
 #print "oid: $oid", "\n";  
       $self->{node}->{$descent}->{ident} = $oid;  
   
   
     } elsif (lc $action eq 'update') {  
         
       # get fresh object from orm first  
       $object = $self->{meta}->{$descent}->{storage}->load($self->{node}->{$descent}->{ident});  
   
 #print Dumper($self->{node});  
         
       # mix in values  
       #print Dumper($object);  
       hash2object($object, $map);  
       #print Dumper($object);  
       #exit;  
       $self->{meta}->{$descent}->{storage}->update($object);  
     }  
   
     my $error = 0;  
   
     # handle new style callbacks - this is a HACK - do this without an eval!  
     #print Dumper($map);  
     #print "cb: ", Dumper($self->{meta}->{$descent}->{Callback});  
     #print Dumper($map_callbacks);  
     foreach my $node (keys %{$map_callbacks->{write}}) {  
       #print Dumper($node);  
       my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_write';  
       my $evalstring = $perl_callback . '( { object => $object, value => $map_callbacks->{write}->{$node}, storage => $self->{meta}->{$descent}->{storage} } );';  
       #print $evalstring, "\n"; exit;  
       eval($evalstring);  
       if ($@) {  
         $error = 1;  
         print $@, "\n";  
       }  
         
       #print "after eval", "\n";  
         
       if (!$error) {  
         # re-update@orm  
         $self->{meta}->{$descent}->{storage}->update($object);  
       }  
     }  
     
     # handle errors  
     if ($error) {  
       #print "error", "\n";  
 =pod  
       my $sqlHandle;  
       #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }  
       $self->{node}->{status}->{error} = {  
         statement => $sql_main,  
         state => $sqlHandle->state,  
         err => $sqlHandle->err,  
         errstr => $sqlHandle->errstr,  
       };  
 =cut  
       # rollback....  
       #print "rollback", "\n";  
       $self->{meta}->{$descent}->{storage}->erase($object);  
       #print "after rollback", "\n";  
     } else {  
       $self->{node}->{status}->{ok} = 1;  
     }  
   
   
   }  
   
 }  
   
 # TODO:  
 # this should be split up into...  
 #   - a "_statNode" (should just touch the node to check for existance)  
 #   - a "_loadNode" (should load node completely)  
 #   - maybe additionally a "loadNodeProperty" (may specify properties to load)  
 #   - introduce $self->{nodecache} for this purpose  
 # TODO:  
 #   should we:  
 #     - not pass ident in here but resolve it via "$descent"?  
 #     - refactor this and stuff it with additional debug/error message  
 #       - this = the way the implicit load mechanism works  
 sub _statloadNode {  
   
   my $self = shift;  
   my $descent = shift;  
   my $ident = shift;  
   my $force = shift;  
   
   # fetch entry to retrieve checksum from  
   # was:  
   if (!$self->{node}->{$descent} || $force) {  
   # is:  
   #if (!$self->{node}->{$descent}->{item} || $force) {  
       
     if (!$ident) {  
       #print "\n", "Attempt to fetch entry implicitely by ident failed: no ident given! This may result in an insert if no write-protection is in the way.", "\n";  
       return;  
     }  
482            
483      # patch for DBD::CSV    # trace
484      if ($ident && $ident eq 'Null') {      #print Dumper($self->{meta});
485        return;      #print Dumper($self->{node});
486      }      #exit;
   
 #print "yai!", "\n";  
   
     my $query = {  
       node => $self->{meta}->{$descent}->{node},  
       subnodes => [qw( cs )],  
       criterias => [  
         { key => $self->{meta}->{$descent}->{IdentProvider}->{arg},  
            op => 'eq',  
            val => $ident },  
       ]  
     };  
   
 #print Dumper($query);  
   
     my $result = $self->{meta}->{$descent}->{storage}->sendQuery($query);  
   
     my $entry = $result->getNextEntry();  
   
 #print Dumper($entry);  
 #print "pers: " . $self->{meta}->{$descent}->{storage}->is_persistent($entry), "\n";  
 #my $state = $self->{meta}->{$descent}->{storage}->_fetch_object_state($entry, { name => 'TransactionHop' } );  
 #print Dumper($state);  
   
     my $status = $result->getStatus();  
   
 #print Dumper($status);  
       
     # TODO: enhance error handling (store inside tc)  
     #if (!$row) {  
     #  print "\n", "row error", "\n";  
     #  next;  
     #}  
   
     # these checks run before actually loading payload- and meta-data to node-container  
487            
       # 1st level - hard error  
       if ($status && $status->{err}) {  
         $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - hard error (that's ok): $status->{err}" );  
         return;  
       }  
     
       # 2nd level - logical (empty/notfound) error  
       if (($status && $status->{empty}) || !$entry) {  
         $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - logical error (that's ok)" );  
         #print "no entry (logical)", "\n";  
         return;  
       }  
   
 #print Dumper($entry);  
   
     # was:  
     # $self->{node}->{$descent}->{ident} = $ident;    
     # is:  
     # TODO: re-resolve ident from entry via metadata "IdentProvider" here - like elsewhere  
     $self->{node}->{$descent}->{ident} = $ident;  
     $self->{node}->{$descent}->{payload} = $entry;  
   
   }  
     
   return 1;  
     
 }  
   
 sub _doTransferToTarget {  
   my $self = shift;  
   my $action = shift;  
488    $self->_modifyNode('target', $action, $self->{node}->{map});    $self->_modifyNode('target', $action, $self->{node}->{map});
489  }  }
490    
491    
492  sub _doModifySource_IdentChecksum {  sub _doModifySource_IdentChecksum {
493    my $self = shift;    my $self = shift;
494    my $ident_new = shift;    my $ident_new = shift;
# Line 1097  sub _doModifySource_IdentChecksum { Line 509  sub _doModifySource_IdentChecksum {
509  }  }
510    
511    
 # this is a shortcut method  
 # ... let's try to avoid _any_ redundant code in here (ok... - at the cost of method lookups...)  
 sub _getNodeList {  
   my $self = shift;  
   my $descent = shift;  
   my $filter = shift;  
   return $self->{meta}->{$descent}->{storage}->getListFiltered($self->{meta}->{$descent}->{node}, $filter);  
 }  
   
512    
513  sub _prepareNode_MetaProperties {  sub _prepareNode_MetaProperties {
514    my $self = shift;    my $self = shift;
# Line 1199  sub _otherSide { Line 602  sub _otherSide {
602    return '';    return '';
603  }  }
604    
 sub _erase_all {  
   my $self = shift;  
   my $descent = shift;  
   #my $node = shift;  
   my $node = $self->{meta}->{$descent}->{node};  
   $self->{meta}->{$descent}->{storage}->eraseAll($node);  
 }  
605    
606  1;  1;
607    __END__

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.6

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