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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (hide annotations)
Thu Mar 27 15:31:15 2003 UTC (21 years, 3 months ago) by joko
Branch: MAIN
Changes since 1.7: +5 -2 lines
fixes to modules regarding new namespace(s) below Data::Mungle::*

1 joko 1.8 ## $Id: API.pm,v 1.7 2003/02/21 08:00:24 joko Exp $
2 joko 1.1 ##
3     ## Copyright (c) 2002 Andreas Motl <andreas.motl@ilo.de>
4     ##
5     ## See COPYRIGHT section in pod text below for usage and distribution rights.
6     ##
7     ## ----------------------------------------------------------------------------------------
8 joko 1.2 ## $Log: API.pm,v $
9 joko 1.8 ## Revision 1.7 2003/02/21 08:00:24 joko
10     ## debugging
11     ##
12 joko 1.7 ## Revision 1.6 2003/02/14 12:58:47 joko
13     ## + re-enabled the erase-mechanism
14     ##
15 joko 1.6 ## Revision 1.5 2003/02/11 05:26:04 joko
16     ## + sub _tellWhatIWillDo
17     ## + re-enabled "branch to execution path for special targets" mechanism
18     ##
19 joko 1.5 ## Revision 1.4 2003/02/09 05:03:02 joko
20     ## + minor fix regarding namespace of api versioning extension module
21     ##
22 joko 1.4 ## Revision 1.3 2003/02/09 04:59:27 joko
23     ## + api versioning mechanism
24     ## + major structure changes
25     ## - refactored code to sister modules
26     ##
27 joko 1.3 ## Revision 1.2 2003/01/20 16:59:48 joko
28     ## + cosmetics and debugging
29     ##
30 joko 1.2 ## Revision 1.1 2003/01/19 01:23:04 joko
31     ## + new from Data/Transfer/Sync.pm
32     ##
33 joko 1.1 ## ----------------------------------------------------------------------------------------
34    
35    
36 joko 1.2 package Data::Transfer::Sync::API;
37    
38 joko 1.1 use strict;
39     use warnings;
40 joko 1.2
41 joko 1.3 use base qw( DesignPattern::Bridge );
42    
43 joko 1.1 use mixin::with qw( Data::Transfer::Sync );
44 joko 1.2
45 joko 1.1
46     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
47 joko 1.2
48     use Data::Dumper;
49 joko 1.3 use Hash::Merge qw( merge );
50 joko 1.2
51 joko 1.8 use Data::Mungle::Compare::Struct qw( isEmpty );
52 joko 1.2
53 joko 1.1 # get logger instance
54     my $logger = Log::Dispatch::Config->instance;
55 joko 1.2
56 joko 1.3
57     sub api_constructor {
58 joko 1.1 my $self = shift;
59 joko 1.3 $logger->debug( __PACKAGE__ . "->api_constructor: Loading API");
60     $self->_loadVersionExtensions();
61 joko 1.1 }
62 joko 1.2
63 joko 1.3
64     sub _loadVersionExtensions {
65 joko 1.1 my $self = shift;
66 joko 1.3 my $syncVersion = $self->{sync_version};
67     $syncVersion ||= '';
68     $logger->debug( __PACKAGE__ . "->loadVersionExtensions( version='$syncVersion' )");
69     #print Dumper($self);
70     #exit;
71 joko 1.4 my $module = "API::$syncVersion";
72 joko 1.3 $self->load($module);
73     }
74    
75    
76     sub configure {
77     my $self = shift;
78    
79     $logger->debug( __PACKAGE__ . "->configure");
80    
81     my @args = @_;
82    
83     if (!isEmpty(\@args)) {
84     my %properties = @_;
85     # merge args to properties
86     #map { $self->{$_} = $properties{$_}; } keys %properties;
87     #print Dumper($self);
88     #print Dumper(\%properties);
89     if ($self->{options}) {
90     my $options_new = merge($self->{options}, \%properties);
91     #print Dumper($options_new);
92     $self->{options} = $options_new;
93     #print Dumper($self->{options});
94     } else {
95     $self->{options} = \%properties;
96     }
97     $self->_init();
98     #$self->_initV1();
99     } else {
100     #print "no args!", "\n";
101 joko 1.1 }
102    
103 joko 1.3 #print Dumper($self);
104     #exit;
105    
106     $self->{state}->{configured} = 1;
107     return 1;
108 joko 1.1 }
109 joko 1.2
110 joko 1.3
111     sub setArguments {
112 joko 1.1 my $self = shift;
113 joko 1.3 my $args_raw = shift;
114     $self->{args_raw} = $args_raw;
115     }
116 joko 1.1
117 joko 1.3 sub readArguments {
118     my $self = shift;
119 joko 1.1
120 joko 1.3 my %syncConfig;
121     tie %syncConfig, 'Tie::IxHash';
122     %syncConfig = (
123     map => {
124     moduleName => $self->{args_raw}->{'mapping-module'},
125     },
126     source => {
127     dbKey => $self->{args_raw}->{source},
128     nodeType => $self->{args_raw}->{'source-type'},
129     nodeName => $self->{args_raw}->{'source-node'},
130     },
131     target => {
132     dbKey => $self->{args_raw}->{target},
133     nodeName => $self->{args_raw}->{'target-node'},
134     },
135     process => {
136     mode => $self->{args_raw}->{mode},
137     erase => $self->{args_raw}->{erase},
138     import => $self->{args_raw}->{import},
139     prepare => $self->{args_raw}->{prepare},
140     },
141     # metadata => {
142     # config => $self->{config_metadata},
143     # }
144     );
145 joko 1.1
146 joko 1.3 $self->{args} = \%syncConfig;
147 joko 1.1
148     }
149    
150 joko 1.2
151 joko 1.3 # TODO: some feature to show off the progress of synchronization (cur/max * 100)
152     sub syncNodes {
153 joko 1.1
154     my $self = shift;
155 joko 1.3 my $args = shift;
156    
157 joko 1.7 #$logger->notice( "========================== " . __PACKAGE__ . "->syncNodes ==============");
158 joko 1.3 $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
159 joko 1.1
160 joko 1.3 #print Dumper($self);
161 joko 1.1 #exit;
162    
163 joko 1.3 #print Dumper($self->{options});
164     $self->_prepareOptions();
165 joko 1.1
166 joko 1.6 # trace
167     #print Dumper($self->{options});
168     #exit;
169 joko 1.1
170 joko 1.3 if (!$self->checkOptions()) {
171     $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
172 joko 1.1 return;
173     }
174    
175 joko 1.3 if (!$self->{state}->{configured}) {
176     $logger->critical( __PACKAGE__ . "->syncNodes: Synchronization object is not configured/initialized correctly." );
177 joko 1.1 return;
178     }
179    
180 joko 1.5 #print Dumper($args);
181    
182 joko 1.3 # remember arguments through the whole processing
183     $self->{args} = $args;
184 joko 1.1
185 joko 1.3 # hash to hold and/or fill in metadata required for the processing
186     $self->{meta} = {};
187 joko 1.1
188 joko 1.3 # hash to sum up results
189     # TODO: re-implement! (sync-statistics???)
190 joko 1.1
191 joko 1.6 #print Dumper($self->{options}->{process});
192    
193 joko 1.3 # detect synchronization method to determine which optical symbol (directed arrow) to use
194     my $mode = $self->{args}->{mode}; # V1
195     $mode ||= $self->{options}->{process}->{mode}; # V2
196     my $direction_arrow = $self->_getDirectedArrow($mode);
197 joko 1.1
198 joko 1.3 # determine code versions
199     my $code_metadata_version;
200     # first, try to use version supplied by mapping-metadata
201     $code_metadata_version = $self->{options}->{metadata}->{version};
202     # if not set, inherit from global 'sync_version'
203     $code_metadata_version ||= $self->{sync_version};
204    
205     # load additional code from versioned namespace into current instance
206     my $dynModule = "Metadata::$code_metadata_version";
207     $self->load($dynModule);
208    
209     # build metadata using versioned code and stuff
210     $self->options2metadata();
211     $self->options2metadata_accessor();
212    
213 joko 1.5 # branch to execution path for special targets
214     # detect for option 'handler' which could be a CODEref
215     if ($self->{options}->{handler} && ref $self->{options}->{handler} eq 'CODE') {
216     $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}");
217     #print Dumper($self);
218     #exit;
219     # don't do this any more - it wasn't very nice somehow ...
220     #$self->{options}->{handler}->($self->{app}, $self->{options});
221     # .... now: better let the parent application scope handle this via callback
222     # not any more required for this: $self->{app} inside here (which isn't the app we mean here)
223     # required for this: getting the options out of here: establish some getter method! ($self->getOptions(...))
224     # so....
225     #$self->{__bridge}->{parent_ref}->
226    
227     # ahh okay, DesignPattern::Bridge moves closer to some Class::Inner!???
228     # so...
229     # similar like above - but it isn't very nice anyway ... (no privateness, but: who cares?)
230     #print Dumper($self->{__bridge});
231     # just take the global application instance and
232     # throw it into the context of the mapping module - this is heavy! ;-) (but again, who cares...)
233 joko 1.6 # TODO: handle this more abstract *sometime*
234 joko 1.5 #$self->{options}->{handler}->($self->{__bridge}->{parent}->{app}, $self->{options});
235     $self->{options}->{handler}->($self->{__bridge}->{parent}->{process}, $self->{options});
236    
237     return;
238     }
239    
240     # TODO: execution path branch V2!!!
241     # option1: wrap this via callback to parent scope (like current impl. mechanism)
242     # option2: branch directly from here (this needs refactoring of the sub handler)
243    
244 joko 1.3 # tracing
245     #print Dumper($self);
246     #exit;
247 joko 1.1
248 joko 1.7 # V1:
249     $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}]" );
250    
251     # V2:
252     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}]";
253     #my $header = ("~.." x 7) . " " . $what . " " . ("~.." x 4);
254     #my $header = ("= " x 7) . " " . $what . " " . ("= " x 4);
255     #my $header = ("~=-_-=" x 3) . " " . $what . " " . ("~=-_-=" x 4);
256     my $header = ("_-=~=-" x 4) . " " . $what . " " . ("_-=~=-" x 4);
257     $logger->notice($header);
258    
259 joko 1.1
260 joko 1.3 return if !$self->buildFieldmapping();
261     return if !$self->_touchNodeSet();
262     return if !$self->_prepare_sync();
263 joko 1.5 $self->_tellWhatIWillDo();
264 joko 1.3
265     # tracing
266     #print Dumper($self);
267     #print Dumper($self->{args});
268     #print Dumper($self->{options});
269     #print Dumper($self->{meta});
270     #print Dumper($self->{metadata});
271     #exit;
272 joko 1.1
273 joko 1.3 # finally, tell the core to start the synchronization process
274     $self->_run();
275    
276     }
277 joko 1.1
278 joko 1.2
279 joko 1.5 my $c_string_default = '';
280     sub c_string {
281     my $value = shift;
282     $value ||= "[$c_string_default]";
283     return $value;
284     }
285    
286     sub _tellWhatIWillDo {
287     my $self = shift;
288    
289    
290     #return;
291    
292     # trace
293     #print Dumper($self->{meta});
294     #exit;
295    
296     $c_string_default = 'n/a';
297     my $source = c_string($self->{opt}->{'source'});
298     my $source_node = c_string($self->{opt}->{'source-node'});
299     my $source_type = c_string($self->{opt}->{'source-type'});
300     my $target = c_string($self->{opt}->{'target'});
301     my $target_node = c_string($self->{opt}->{'target-node'});
302     my $target_type = c_string($self->{opt}->{'target-type'});
303    
304     my $mapping_module = c_string($self->{opt}->{'mapping-module'});
305     my $mode = uc c_string($self->{opt}->{'mode'});
306    
307     #my $ql = "$mode INTO $source NODE $source_node TYPE $source_type SELECT NODE $target_node TYPE $target_type FROM $target USING MODULE $mapping_module;";
308     #$logger->notice( __PACKAGE__ . ": $ql" );
309     my $ql = <<EOT;
310    
311     FETCH DATA
312     FROM STORAGE $self->{meta}->{source}->{dbKey}
313     AT NODE $self->{meta}->{source}->{accessorName}.$self->{meta}->{source}->{nodeName}
314     USING IDENTIFIER $self->{meta}->{source}->{IdentProvider}->{method}.$self->{meta}->{source}->{IdentProvider}->{arg}
315     CONVERT DATA
316     CAST FROM $self->{meta}->{source}->{nodeType} TO $self->{meta}->{target}->{nodeType}
317     MAP ATTRIBUTES FROM @{$self->{meta}->{source}->{childnodes}} TO @{$self->{meta}->{target}->{childnodes}}
318     STORE DATA
319     TO STORAGE $self->{meta}->{target}->{dbKey}
320     AT NODE $self->{meta}->{target}->{accessorName}.$self->{meta}->{target}->{nodeName}
321     USING IDENTIFIER $self->{meta}->{target}->{IdentProvider}->{method}.$self->{meta}->{target}->{IdentProvider}->{arg}
322     EOT
323    
324    
325 joko 1.7 chomp($ql);
326     $logger->info($ql);
327 joko 1.5
328     #exit;
329     return;
330    
331     my $actioning = ucfirst $self->{opt}->{'action'} . 'ing';
332    
333     # FIXME: this is weird!
334     my $long = <<EOT;
335    
336     - $actioning data of type $target_type and
337     filtered by $target_node from the storage named $target
338     to the storage named $source - filtered by $source_node.
339     - Will attempt to convert the data to $source_type.
340     EOT
341     chomp($long);
342     $logger->notice( __PACKAGE__ . ": $long" );
343    
344     }
345    
346    
347 joko 1.3 sub _prepareOptions {
348 joko 1.1
349     my $self = shift;
350 joko 1.3
351     my $opts = $self->{args};
352 joko 1.1
353     # patch options
354 joko 1.3 $opts->{source}->{nodeName} ||= '';
355     $opts->{target}->{nodeName} ||= '';
356 joko 1.1 $opts->{process}->{mode} ||= '';
357 joko 1.3 $opts->{process}->{erase} ||= 0;
358 joko 1.1 $opts->{process}->{prepare} ||= 0;
359 joko 1.3
360     # defaults (mostly for backward-compatibility to V1 -
361     # but code mungled here out of prepareOptions_V1 from Version::V1)
362     $opts->{metadata}->{syncMethod} ||= 'checksum'; # | timestamp
363     $opts->{source}->{ident} ||= 'storage_method:id';
364     $opts->{source}->{exclude} ||= [qw( cs )];
365     $opts->{target}->{ident} ||= 'property:oid';
366     #$map->{source_node} ||= $source_node_name;
367     #$map->{direction} ||= $opts->{mode}; # | PUSH | PULL | FULL
368 joko 1.1
369     # pre-check options
370     if (!$self->_preCheckOptions($opts)) {
371 joko 1.3 $logger->error( __PACKAGE__ . "->_prepareOptions: _preCheckOptions failed.");
372 joko 1.1 return;
373     }
374    
375     # inform user about option preparation
376 joko 1.3 $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}' )");
377 joko 1.1
378     # try to load mapping-metadata-container
379 joko 1.3 # How? Create a new instance of the given
380     # perl module/package name in ->{...}->{moduleName}.
381     # This instance is used later in the innards of the sync,
382     # that's why the module (class) should have a certain layout
383     # enabling the core to use it for gathering metadata while processing.
384     my $mapObject = DesignPattern::Object->fromPackage($opts->{map}->{moduleName});
385 joko 1.1
386     # try to resolve map from metadata-container
387    
388     # type of the item/node
389     my $source_nodeType = $opts->{source}->{nodeType};
390    
391     # check if mapping for certain node is contained in mapping object
392 joko 1.3 if (!$mapObject || !$mapObject->can($source_nodeType)) {
393     $logger->warning( __PACKAGE__ . "->_prepareOptions: Can't access mapping for source-type=\"$source_nodeType\" - please check \"$opts->{map}->{moduleName}\".");
394 joko 1.1 return;
395     }
396    
397     # get map
398     my $map = $mapObject->$source_nodeType;
399     #print Dumper($map);
400    
401 joko 1.3 my $map_version = $map->{metadata}->{version};
402     # FIXME: backward compatibility
403     if (!$map_version) {
404     $self->options_to_V2($map);
405     }
406    
407     # trace
408     #print Dumper($map);
409     #exit;
410     #print "ref: ", ref $map->{target}, "\n";
411     #print "target: ", $map->{target}, "\n";
412    
413 joko 1.1 # check here if "target" is actually a CODEref - in this case: resolve it - deprecated!!! ???
414     if (ref $map->{target}->{address} eq 'CODE') {
415     $map->{target}->{address} = $map->{target}->{address}->($source_nodeType);
416     }
417    
418     # resolve expressions (on nodename-level) here
419 joko 1.3 elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
420 joko 1.1 my $target_dynamic_type = $1;
421     my $target_dynamic_expression = $2;
422     if (lc $target_dynamic_type eq 'code') {
423     $map->{target} = $mapObject->$target_dynamic_expression($map);
424     }
425     }
426    
427 joko 1.3 # merging - V1
428     #map { $opts->{$_} = $map->{$_}; } keys %$map;
429     # trace
430     #print Dumper($self->{options});
431    
432     # merging - V2
433    
434     # merge local map with local opts
435 joko 1.5
436     # delete undef'd items in $map
437    
438 joko 1.3 # enable cloning
439     # FIXME: do we really need cloning here? trade safety/encapsulation for speed?
440     Hash::Merge::set_clone_behavior(1);
441     Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );
442 joko 1.5 #Hash::Merge::set_behavior( 'STORAGE_PRECEDENT' );
443     #Hash::Merge::set_behavior( 'RETAINMENT_PRECEDENT' );
444     # TODO: add an option to Hash::Merge not to overwrite set items with undefined/empty/not assigned ones
445 joko 1.3 my $locals_merged = merge( $opts, $map );
446    
447     # trace
448     #print Dumper($opts);
449     #print Dumper($map);
450     #print Dumper($locals_merged);
451     #exit;
452    
453     # merge local-merged ones with instance-wide options
454     Hash::Merge::set_clone_behavior(0);
455     $self->{options} = merge( $self->{options}, $locals_merged );
456    
457     # trace
458     #print Dumper($self->{options});
459     #exit;
460    
461 joko 1.1
462     $self->{state}->{options_ready} = 1;
463    
464     return 1;
465    
466     }
467 joko 1.2
468    
469 joko 1.3 sub _preCheckOptions {
470    
471 joko 1.1 my $self = shift;
472 joko 1.3 my $opts = shift;
473    
474     # trace
475     #print Dumper($opts);
476     #exit;
477    
478     if (!$opts->{process}->{mode}) {
479     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"--action=(load|save)\".");
480     return;
481 joko 1.1 }
482    
483 joko 1.3 # the type of the to-be-synced item
484     if (!$opts->{source}->{nodeType}) {
485     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-type\".");
486     return;
487 joko 1.1 }
488 joko 1.3 # the name of the (container-) node the items are listed in
489     if (!$opts->{source}->{nodeName}) {
490     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-node\".");
491     return;
492 joko 1.2 }
493 joko 1.1
494 joko 1.3 # a "map"-declaration which module to use for mapping- and/or lookup-purposes
495     if (!$opts->{map}) {
496     $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");
497 joko 1.1 return;
498     }
499 joko 1.3 if (!$opts->{map}->{moduleName}) {
500     $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module=My::Mapping::Module'.");
501     return;
502 joko 1.1 }
503    
504 joko 1.3 return 1;
505 joko 1.2
506     }
507 joko 1.1
508    
509 joko 1.2
510 joko 1.3 sub _prepare_sync {
511 joko 1.2
512     my $self = shift;
513 joko 1.1
514     # TODO:
515     # + if action == PUSH: start processing
516     # -+ if action == PULL: swap metadata and start processing
517     # - if action == FULL: start processing, then swap metadata and (re-)start processing
518    
519 joko 1.3 #print "dir: ", $self->{args}->{direction}, "\n";
520    
521 joko 1.1 # manipulate metainfo according to direction of synchronization
522 joko 1.3 if (lc $self->{options}->{process}->{mode} eq 'push') {
523     # just do it ... (don't modify any metadata)
524    
525     } elsif (lc $self->{options}->{process}->{mode} eq 'pull') {
526     # swap source and target metadata
527     # TODO: introduce different mechanism to make more then two partners (descents) possible
528 joko 1.1 ($self->{meta}->{source}, $self->{meta}->{target}) =
529     ($self->{meta}->{target}, $self->{meta}->{source});
530 joko 1.5 #($self->{options}->{source}, $self->{options}->{target}) =
531     # ($self->{options}->{target}, $self->{options}->{source});
532 joko 1.3
533     } elsif (lc $self->{options}->{process}->{mode} eq 'full') {
534     # TODO:
535     # do a pull first and a push afterwards
536     # this requires us to be called somehow recursively - just one recursion level ;-)
537    
538 joko 1.1 } else {
539 joko 1.3 # TODO: are there any other synchronization modes besides PULL, PUSH, FULL?
540    
541 joko 1.1 }
542    
543     # import flag means: prepare the source node to be syncable
544     # this is useful if there are e.g. no "ident" or "checksum" columns yet inside a DBI like (row-based) storage
545 joko 1.6 if ($self->{options}->{process}->{prepare}) {
546 joko 1.1 $self->_prepareNode_MetaProperties('source');
547     $self->_prepareNode_DummyIdent('source');
548     #return;
549     #$self->_erase_all($opts->{source_node});
550     }
551    
552     # erase flag means: erase the target
553     #if ($opts->{erase}) {
554 joko 1.6 if ($self->{options}->{process}->{erase}) {
555 joko 1.1 # TODO: move this method to the scope of the synchronization core and wrap it around different handlers
556     #print "ERASE", "\n";
557     $self->_erase_all('target');
558     }
559 joko 1.2
560     return 1;
561 joko 1.1
562 joko 1.3 }
563    
564     sub _getDirectedArrow {
565     my $self = shift;
566     my $mode = shift;
567     $mode ||= '';
568    
569     if (lc $mode eq 'push') {
570     return '->';
571     } elsif (lc $mode eq 'pull') {
572     return '<-';
573     } elsif (lc $mode eq 'full') {
574     return '<->';
575     } else {
576     return '';
577     }
578 joko 1.1 }
579    
580     1;

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