/[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.3 - (show annotations)
Sun Feb 9 04:59:27 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.2: +275 -300 lines
+ api versioning mechanism
+ major structure changes
- refactored code to sister modules

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

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