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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Tue Feb 11 05:26:04 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.4: +113 -7 lines
+ sub _tellWhatIWillDo
+ re-enabled "branch to execution path for special targets" mechanism

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

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