/[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.3 - (hide 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 joko 1.3 ## $Id: API.pm,v 1.2 2003/01/20 16:59:48 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.3 ## Revision 1.2 2003/01/20 16:59:48 joko
10     ## + cosmetics and debugging
11     ##
12 joko 1.2 ## Revision 1.1 2003/01/19 01:23:04 joko
13     ## + new from Data/Transfer/Sync.pm
14     ##
15 joko 1.1 ## ----------------------------------------------------------------------------------------
16    
17    
18 joko 1.2 package Data::Transfer::Sync::API;
19    
20 joko 1.1 use strict;
21     use warnings;
22 joko 1.2
23 joko 1.3 use base qw( DesignPattern::Bridge );
24    
25 joko 1.1 use mixin::with qw( Data::Transfer::Sync );
26 joko 1.2
27 joko 1.1
28     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
29 joko 1.2
30     use Data::Dumper;
31 joko 1.3 use Hash::Merge qw( merge );
32 joko 1.2
33 joko 1.3 use Data::Compare::Struct qw( isEmpty );
34 joko 1.2
35 joko 1.1 # get logger instance
36     my $logger = Log::Dispatch::Config->instance;
37 joko 1.2
38 joko 1.3
39     sub api_constructor {
40 joko 1.1 my $self = shift;
41 joko 1.3 $logger->debug( __PACKAGE__ . "->api_constructor: Loading API");
42     $self->_loadVersionExtensions();
43 joko 1.1 }
44 joko 1.2
45 joko 1.3
46     sub _loadVersionExtensions {
47 joko 1.1 my $self = shift;
48 joko 1.3 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 joko 1.1 }
90    
91 joko 1.3 #print Dumper($self);
92     #exit;
93    
94     $self->{state}->{configured} = 1;
95     return 1;
96 joko 1.1 }
97 joko 1.2
98 joko 1.3
99     sub setArguments {
100 joko 1.1 my $self = shift;
101 joko 1.3 my $args_raw = shift;
102     $self->{args_raw} = $args_raw;
103     }
104 joko 1.1
105 joko 1.3 sub readArguments {
106     my $self = shift;
107 joko 1.1
108 joko 1.3 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 joko 1.1
134 joko 1.3 $self->{args} = \%syncConfig;
135 joko 1.1
136     }
137    
138 joko 1.2
139 joko 1.3 # TODO: some feature to show off the progress of synchronization (cur/max * 100)
140     sub syncNodes {
141 joko 1.1
142     my $self = shift;
143 joko 1.3 my $args = shift;
144    
145     $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
146 joko 1.1
147 joko 1.3 #print Dumper($self);
148 joko 1.1 #exit;
149    
150 joko 1.3 #print Dumper($self->{options});
151     $self->_prepareOptions();
152 joko 1.1
153 joko 1.3 #print Dumper($self->{options});
154 joko 1.1
155 joko 1.3 if (!$self->checkOptions()) {
156     $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
157 joko 1.1 return;
158     }
159    
160 joko 1.3 if (!$self->{state}->{configured}) {
161     $logger->critical( __PACKAGE__ . "->syncNodes: Synchronization object is not configured/initialized correctly." );
162 joko 1.1 return;
163     }
164    
165 joko 1.3 # remember arguments through the whole processing
166     $self->{args} = $args;
167 joko 1.1
168 joko 1.3 # hash to hold and/or fill in metadata required for the processing
169     $self->{meta} = {};
170 joko 1.1
171 joko 1.3 # hash to sum up results
172     # TODO: re-implement! (sync-statistics???)
173 joko 1.1
174 joko 1.3 # 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 joko 1.1
179 joko 1.3 # 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 joko 1.1
198 joko 1.3 $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 joko 1.1
200 joko 1.3 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 joko 1.1
212 joko 1.3 # finally, tell the core to start the synchronization process
213     $self->_run();
214    
215     }
216 joko 1.1
217 joko 1.2
218 joko 1.3 sub _prepareOptions {
219 joko 1.1
220     my $self = shift;
221 joko 1.3
222     my $opts = $self->{args};
223 joko 1.1
224     # patch options
225 joko 1.3 $opts->{source}->{nodeName} ||= '';
226     $opts->{target}->{nodeName} ||= '';
227 joko 1.1 $opts->{process}->{mode} ||= '';
228 joko 1.3 $opts->{process}->{erase} ||= 0;
229 joko 1.1 $opts->{process}->{prepare} ||= 0;
230 joko 1.3
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 joko 1.1
240     # pre-check options
241     if (!$self->_preCheckOptions($opts)) {
242 joko 1.3 $logger->error( __PACKAGE__ . "->_prepareOptions: _preCheckOptions failed.");
243 joko 1.1 return;
244     }
245    
246     # inform user about option preparation
247 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}' )");
248 joko 1.1
249     # try to load mapping-metadata-container
250 joko 1.3 # 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 joko 1.1
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 joko 1.3 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 joko 1.1 return;
266     }
267    
268     # get map
269     my $map = $mapObject->$source_nodeType;
270     #print Dumper($map);
271    
272 joko 1.3 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 joko 1.1 # 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 joko 1.3 elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
291 joko 1.1 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 joko 1.3 # 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 joko 1.1
327     $self->{state}->{options_ready} = 1;
328    
329     return 1;
330    
331     }
332 joko 1.2
333    
334 joko 1.3 sub _preCheckOptions {
335    
336 joko 1.1 my $self = shift;
337 joko 1.3 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 joko 1.1 }
347    
348 joko 1.3 # 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 joko 1.1 }
353 joko 1.3 # 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 joko 1.2 }
358 joko 1.1
359 joko 1.3 # 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 joko 1.1 return;
363     }
364 joko 1.3 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 joko 1.1 }
368    
369 joko 1.3 return 1;
370 joko 1.2
371     }
372 joko 1.1
373    
374 joko 1.2
375 joko 1.3 sub _prepare_sync {
376 joko 1.2
377     my $self = shift;
378 joko 1.1
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 joko 1.3 #print "dir: ", $self->{args}->{direction}, "\n";
385    
386 joko 1.1 # manipulate metainfo according to direction of synchronization
387 joko 1.3 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 joko 1.1 ($self->{meta}->{source}, $self->{meta}->{target}) =
394     ($self->{meta}->{target}, $self->{meta}->{source});
395 joko 1.3
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 joko 1.1 } else {
402 joko 1.3 # TODO: are there any other synchronization modes besides PULL, PUSH, FULL?
403    
404 joko 1.1 }
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 joko 1.2
423     return 1;
424 joko 1.1
425 joko 1.3 }
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 joko 1.1 }
442    
443     1;

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