/[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.6 - (show annotations)
Fri Feb 14 12:58:47 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.5: +14 -5 lines
+ re-enabled the erase-mechanism

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

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