/[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.4 - (show annotations)
Sun Feb 9 05:03:02 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.3: +7 -2 lines
+ minor fix regarding namespace of api versioning extension module

1 ## $Id: API.pm,v 1.3 2003/02/09 04:59:27 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.3 2003/02/09 04:59:27 joko
10 ## + api versioning mechanism
11 ## + major structure changes
12 ## - refactored code to sister modules
13 ##
14 ## Revision 1.2 2003/01/20 16:59:48 joko
15 ## + cosmetics and debugging
16 ##
17 ## Revision 1.1 2003/01/19 01:23:04 joko
18 ## + new from Data/Transfer/Sync.pm
19 ##
20 ## ----------------------------------------------------------------------------------------
21
22
23 package Data::Transfer::Sync::API;
24
25 use strict;
26 use warnings;
27
28 use base qw( DesignPattern::Bridge );
29
30 use mixin::with qw( Data::Transfer::Sync );
31
32
33 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
34
35 use Data::Dumper;
36 use Hash::Merge qw( merge );
37
38 use Data::Compare::Struct qw( isEmpty );
39
40 # get logger instance
41 my $logger = Log::Dispatch::Config->instance;
42
43
44 sub api_constructor {
45 my $self = shift;
46 $logger->debug( __PACKAGE__ . "->api_constructor: Loading API");
47 $self->_loadVersionExtensions();
48 }
49
50
51 sub _loadVersionExtensions {
52 my $self = shift;
53 my $syncVersion = $self->{sync_version};
54 $syncVersion ||= '';
55 $logger->debug( __PACKAGE__ . "->loadVersionExtensions( version='$syncVersion' )");
56 #print Dumper($self);
57 #exit;
58 my $module = "API::$syncVersion";
59 $self->load($module);
60 }
61
62
63 sub configure {
64 my $self = shift;
65
66 #print "YAI\n";
67 #print Dumper(@_);
68 #exit;
69
70 $logger->debug( __PACKAGE__ . "->configure");
71
72 my @args = @_;
73
74 #print Dumper(@args);
75
76 if (!isEmpty(\@args)) {
77 my %properties = @_;
78 # merge args to properties
79 #map { $self->{$_} = $properties{$_}; } keys %properties;
80 #print Dumper($self);
81 #print Dumper(\%properties);
82 if ($self->{options}) {
83 my $options_new = merge($self->{options}, \%properties);
84 #print Dumper($options_new);
85 $self->{options} = $options_new;
86 #print Dumper($self->{options});
87 } else {
88 $self->{options} = \%properties;
89 }
90 $self->_init();
91 #$self->_initV1();
92 } else {
93 #print "no args!", "\n";
94 }
95
96 #print Dumper($self);
97 #exit;
98
99 $self->{state}->{configured} = 1;
100 return 1;
101 }
102
103
104 sub setArguments {
105 my $self = shift;
106 my $args_raw = shift;
107 $self->{args_raw} = $args_raw;
108 }
109
110 sub readArguments {
111 my $self = shift;
112
113 my %syncConfig;
114 tie %syncConfig, 'Tie::IxHash';
115 %syncConfig = (
116 map => {
117 moduleName => $self->{args_raw}->{'mapping-module'},
118 },
119 source => {
120 dbKey => $self->{args_raw}->{source},
121 nodeType => $self->{args_raw}->{'source-type'},
122 nodeName => $self->{args_raw}->{'source-node'},
123 },
124 target => {
125 dbKey => $self->{args_raw}->{target},
126 nodeName => $self->{args_raw}->{'target-node'},
127 },
128 process => {
129 mode => $self->{args_raw}->{mode},
130 erase => $self->{args_raw}->{erase},
131 import => $self->{args_raw}->{import},
132 prepare => $self->{args_raw}->{prepare},
133 },
134 # metadata => {
135 # config => $self->{config_metadata},
136 # }
137 );
138
139 $self->{args} = \%syncConfig;
140
141 }
142
143
144 # TODO: some feature to show off the progress of synchronization (cur/max * 100)
145 sub syncNodes {
146
147 my $self = shift;
148 my $args = shift;
149
150 $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
151
152 #print Dumper($self);
153 #exit;
154
155 #print Dumper($self->{options});
156 $self->_prepareOptions();
157
158 #print Dumper($self->{options});
159
160 if (!$self->checkOptions()) {
161 $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
162 return;
163 }
164
165 if (!$self->{state}->{configured}) {
166 $logger->critical( __PACKAGE__ . "->syncNodes: Synchronization object is not configured/initialized correctly." );
167 return;
168 }
169
170 # remember arguments through the whole processing
171 $self->{args} = $args;
172
173 # hash to hold and/or fill in metadata required for the processing
174 $self->{meta} = {};
175
176 # hash to sum up results
177 # TODO: re-implement! (sync-statistics???)
178
179 # detect synchronization method to determine which optical symbol (directed arrow) to use
180 my $mode = $self->{args}->{mode}; # V1
181 $mode ||= $self->{options}->{process}->{mode}; # V2
182 my $direction_arrow = $self->_getDirectedArrow($mode);
183
184 # determine code versions
185 my $code_metadata_version;
186 # first, try to use version supplied by mapping-metadata
187 $code_metadata_version = $self->{options}->{metadata}->{version};
188 # if not set, inherit from global 'sync_version'
189 $code_metadata_version ||= $self->{sync_version};
190
191 # load additional code from versioned namespace into current instance
192 my $dynModule = "Metadata::$code_metadata_version";
193 $self->load($dynModule);
194
195 # build metadata using versioned code and stuff
196 $self->options2metadata();
197 $self->options2metadata_accessor();
198
199 # tracing
200 #print Dumper($self);
201 #exit;
202
203 $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}]" );
204
205 return if !$self->buildFieldmapping();
206 return if !$self->_touchNodeSet();
207 return if !$self->_prepare_sync();
208
209 # tracing
210 #print Dumper($self);
211 #print Dumper($self->{args});
212 #print Dumper($self->{options});
213 #print Dumper($self->{meta});
214 #print Dumper($self->{metadata});
215 #exit;
216
217 # finally, tell the core to start the synchronization process
218 $self->_run();
219
220 }
221
222
223 sub _prepareOptions {
224
225 my $self = shift;
226
227 my $opts = $self->{args};
228
229 # patch options
230 $opts->{source}->{nodeName} ||= '';
231 $opts->{target}->{nodeName} ||= '';
232 $opts->{process}->{mode} ||= '';
233 $opts->{process}->{erase} ||= 0;
234 $opts->{process}->{prepare} ||= 0;
235
236 # defaults (mostly for backward-compatibility to V1 -
237 # but code mungled here out of prepareOptions_V1 from Version::V1)
238 $opts->{metadata}->{syncMethod} ||= 'checksum'; # | timestamp
239 $opts->{source}->{ident} ||= 'storage_method:id';
240 $opts->{source}->{exclude} ||= [qw( cs )];
241 $opts->{target}->{ident} ||= 'property:oid';
242 #$map->{source_node} ||= $source_node_name;
243 #$map->{direction} ||= $opts->{mode}; # | PUSH | PULL | FULL
244
245 # pre-check options
246 if (!$self->_preCheckOptions($opts)) {
247 $logger->error( __PACKAGE__ . "->_prepareOptions: _preCheckOptions failed.");
248 return;
249 }
250
251 # inform user about option preparation
252 $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}' )");
253
254 # try to load mapping-metadata-container
255 # How? Create a new instance of the given
256 # perl module/package name in ->{...}->{moduleName}.
257 # This instance is used later in the innards of the sync,
258 # that's why the module (class) should have a certain layout
259 # enabling the core to use it for gathering metadata while processing.
260 my $mapObject = DesignPattern::Object->fromPackage($opts->{map}->{moduleName});
261
262 # try to resolve map from metadata-container
263
264 # type of the item/node
265 my $source_nodeType = $opts->{source}->{nodeType};
266
267 # check if mapping for certain node is contained in mapping object
268 if (!$mapObject || !$mapObject->can($source_nodeType)) {
269 $logger->warning( __PACKAGE__ . "->_prepareOptions: Can't access mapping for source-type=\"$source_nodeType\" - please check \"$opts->{map}->{moduleName}\".");
270 return;
271 }
272
273 # get map
274 my $map = $mapObject->$source_nodeType;
275 #print Dumper($map);
276
277 my $map_version = $map->{metadata}->{version};
278 # FIXME: backward compatibility
279 if (!$map_version) {
280 $self->options_to_V2($map);
281 }
282
283 # trace
284 #print Dumper($map);
285 #exit;
286 #print "ref: ", ref $map->{target}, "\n";
287 #print "target: ", $map->{target}, "\n";
288
289 # check here if "target" is actually a CODEref - in this case: resolve it - deprecated!!! ???
290 if (ref $map->{target}->{address} eq 'CODE') {
291 $map->{target}->{address} = $map->{target}->{address}->($source_nodeType);
292 }
293
294 # resolve expressions (on nodename-level) here
295 elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
296 my $target_dynamic_type = $1;
297 my $target_dynamic_expression = $2;
298 if (lc $target_dynamic_type eq 'code') {
299 $map->{target} = $mapObject->$target_dynamic_expression($map);
300 }
301 }
302
303 # merging - V1
304 #map { $opts->{$_} = $map->{$_}; } keys %$map;
305 # trace
306 #print Dumper($self->{options});
307
308 # merging - V2
309
310 # merge local map with local opts
311 # enable cloning
312 # FIXME: do we really need cloning here? trade safety/encapsulation for speed?
313 Hash::Merge::set_clone_behavior(1);
314 Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );
315 my $locals_merged = merge( $opts, $map );
316
317 # trace
318 #print Dumper($opts);
319 #print Dumper($map);
320 #print Dumper($locals_merged);
321 #exit;
322
323 # merge local-merged ones with instance-wide options
324 Hash::Merge::set_clone_behavior(0);
325 $self->{options} = merge( $self->{options}, $locals_merged );
326
327 # trace
328 #print Dumper($self->{options});
329 #exit;
330
331
332 $self->{state}->{options_ready} = 1;
333
334 return 1;
335
336 }
337
338
339 sub _preCheckOptions {
340
341 my $self = shift;
342 my $opts = shift;
343
344 # trace
345 #print Dumper($opts);
346 #exit;
347
348 if (!$opts->{process}->{mode}) {
349 $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"--action=(load|save)\".");
350 return;
351 }
352
353 # the type of the to-be-synced item
354 if (!$opts->{source}->{nodeType}) {
355 $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-type\".");
356 return;
357 }
358 # the name of the (container-) node the items are listed in
359 if (!$opts->{source}->{nodeName}) {
360 $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-node\".");
361 return;
362 }
363
364 # a "map"-declaration which module to use for mapping- and/or lookup-purposes
365 if (!$opts->{map}) {
366 $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");
367 return;
368 }
369 if (!$opts->{map}->{moduleName}) {
370 $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module=My::Mapping::Module'.");
371 return;
372 }
373
374 return 1;
375
376 }
377
378
379
380 sub _prepare_sync {
381
382 my $self = shift;
383
384 # TODO:
385 # + if action == PUSH: start processing
386 # -+ if action == PULL: swap metadata and start processing
387 # - if action == FULL: start processing, then swap metadata and (re-)start processing
388
389 #print "dir: ", $self->{args}->{direction}, "\n";
390
391 # manipulate metainfo according to direction of synchronization
392 if (lc $self->{options}->{process}->{mode} eq 'push') {
393 # just do it ... (don't modify any metadata)
394
395 } elsif (lc $self->{options}->{process}->{mode} eq 'pull') {
396 # swap source and target metadata
397 # TODO: introduce different mechanism to make more then two partners (descents) possible
398 ($self->{meta}->{source}, $self->{meta}->{target}) =
399 ($self->{meta}->{target}, $self->{meta}->{source});
400
401 } elsif (lc $self->{options}->{process}->{mode} eq 'full') {
402 # TODO:
403 # do a pull first and a push afterwards
404 # this requires us to be called somehow recursively - just one recursion level ;-)
405
406 } else {
407 # TODO: are there any other synchronization modes besides PULL, PUSH, FULL?
408
409 }
410
411 # import flag means: prepare the source node to be syncable
412 # this is useful if there are e.g. no "ident" or "checksum" columns yet inside a DBI like (row-based) storage
413 if ($self->{args}->{prepare}) {
414 $self->_prepareNode_MetaProperties('source');
415 $self->_prepareNode_DummyIdent('source');
416 #return;
417 #$self->_erase_all($opts->{source_node});
418 }
419
420 # erase flag means: erase the target
421 #if ($opts->{erase}) {
422 if ($self->{args}->{erase}) {
423 # TODO: move this method to the scope of the synchronization core and wrap it around different handlers
424 #print "ERASE", "\n";
425 $self->_erase_all('target');
426 }
427
428 return 1;
429
430 }
431
432 sub _getDirectedArrow {
433 my $self = shift;
434 my $mode = shift;
435 $mode ||= '';
436
437 if (lc $mode eq 'push') {
438 return '->';
439 } elsif (lc $mode eq 'pull') {
440 return '<-';
441 } elsif (lc $mode eq 'full') {
442 return '<->';
443 } else {
444 return '';
445 }
446 }
447
448 1;

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