/[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.4 - (hide 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 joko 1.4 ## $Id: API.pm,v 1.3 2003/02/09 04:59:27 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.4 ## 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 joko 1.3 ## Revision 1.2 2003/01/20 16:59:48 joko
15     ## + cosmetics and debugging
16     ##
17 joko 1.2 ## Revision 1.1 2003/01/19 01:23:04 joko
18     ## + new from Data/Transfer/Sync.pm
19     ##
20 joko 1.1 ## ----------------------------------------------------------------------------------------
21    
22    
23 joko 1.2 package Data::Transfer::Sync::API;
24    
25 joko 1.1 use strict;
26     use warnings;
27 joko 1.2
28 joko 1.3 use base qw( DesignPattern::Bridge );
29    
30 joko 1.1 use mixin::with qw( Data::Transfer::Sync );
31 joko 1.2
32 joko 1.1
33     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
34 joko 1.2
35     use Data::Dumper;
36 joko 1.3 use Hash::Merge qw( merge );
37 joko 1.2
38 joko 1.3 use Data::Compare::Struct qw( isEmpty );
39 joko 1.2
40 joko 1.1 # get logger instance
41     my $logger = Log::Dispatch::Config->instance;
42 joko 1.2
43 joko 1.3
44     sub api_constructor {
45 joko 1.1 my $self = shift;
46 joko 1.3 $logger->debug( __PACKAGE__ . "->api_constructor: Loading API");
47     $self->_loadVersionExtensions();
48 joko 1.1 }
49 joko 1.2
50 joko 1.3
51     sub _loadVersionExtensions {
52 joko 1.1 my $self = shift;
53 joko 1.3 my $syncVersion = $self->{sync_version};
54     $syncVersion ||= '';
55     $logger->debug( __PACKAGE__ . "->loadVersionExtensions( version='$syncVersion' )");
56     #print Dumper($self);
57     #exit;
58 joko 1.4 my $module = "API::$syncVersion";
59 joko 1.3 $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 joko 1.1 }
95    
96 joko 1.3 #print Dumper($self);
97     #exit;
98    
99     $self->{state}->{configured} = 1;
100     return 1;
101 joko 1.1 }
102 joko 1.2
103 joko 1.3
104     sub setArguments {
105 joko 1.1 my $self = shift;
106 joko 1.3 my $args_raw = shift;
107     $self->{args_raw} = $args_raw;
108     }
109 joko 1.1
110 joko 1.3 sub readArguments {
111     my $self = shift;
112 joko 1.1
113 joko 1.3 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 joko 1.1
139 joko 1.3 $self->{args} = \%syncConfig;
140 joko 1.1
141     }
142    
143 joko 1.2
144 joko 1.3 # TODO: some feature to show off the progress of synchronization (cur/max * 100)
145     sub syncNodes {
146 joko 1.1
147     my $self = shift;
148 joko 1.3 my $args = shift;
149    
150     $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
151 joko 1.1
152 joko 1.3 #print Dumper($self);
153 joko 1.1 #exit;
154    
155 joko 1.3 #print Dumper($self->{options});
156     $self->_prepareOptions();
157 joko 1.1
158 joko 1.3 #print Dumper($self->{options});
159 joko 1.1
160 joko 1.3 if (!$self->checkOptions()) {
161     $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
162 joko 1.1 return;
163     }
164    
165 joko 1.3 if (!$self->{state}->{configured}) {
166     $logger->critical( __PACKAGE__ . "->syncNodes: Synchronization object is not configured/initialized correctly." );
167 joko 1.1 return;
168     }
169    
170 joko 1.3 # remember arguments through the whole processing
171     $self->{args} = $args;
172 joko 1.1
173 joko 1.3 # hash to hold and/or fill in metadata required for the processing
174     $self->{meta} = {};
175 joko 1.1
176 joko 1.3 # hash to sum up results
177     # TODO: re-implement! (sync-statistics???)
178 joko 1.1
179 joko 1.3 # 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 joko 1.1
184 joko 1.3 # 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 joko 1.1
203 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}]" );
204 joko 1.1
205 joko 1.3 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 joko 1.1
217 joko 1.3 # finally, tell the core to start the synchronization process
218     $self->_run();
219    
220     }
221 joko 1.1
222 joko 1.2
223 joko 1.3 sub _prepareOptions {
224 joko 1.1
225     my $self = shift;
226 joko 1.3
227     my $opts = $self->{args};
228 joko 1.1
229     # patch options
230 joko 1.3 $opts->{source}->{nodeName} ||= '';
231     $opts->{target}->{nodeName} ||= '';
232 joko 1.1 $opts->{process}->{mode} ||= '';
233 joko 1.3 $opts->{process}->{erase} ||= 0;
234 joko 1.1 $opts->{process}->{prepare} ||= 0;
235 joko 1.3
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 joko 1.1
245     # pre-check options
246     if (!$self->_preCheckOptions($opts)) {
247 joko 1.3 $logger->error( __PACKAGE__ . "->_prepareOptions: _preCheckOptions failed.");
248 joko 1.1 return;
249     }
250    
251     # inform user about option preparation
252 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}' )");
253 joko 1.1
254     # try to load mapping-metadata-container
255 joko 1.3 # 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 joko 1.1
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 joko 1.3 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 joko 1.1 return;
271     }
272    
273     # get map
274     my $map = $mapObject->$source_nodeType;
275     #print Dumper($map);
276    
277 joko 1.3 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 joko 1.1 # 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 joko 1.3 elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
296 joko 1.1 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 joko 1.3 # 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 joko 1.1
332     $self->{state}->{options_ready} = 1;
333    
334     return 1;
335    
336     }
337 joko 1.2
338    
339 joko 1.3 sub _preCheckOptions {
340    
341 joko 1.1 my $self = shift;
342 joko 1.3 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 joko 1.1 }
352    
353 joko 1.3 # 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 joko 1.1 }
358 joko 1.3 # 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 joko 1.2 }
363 joko 1.1
364 joko 1.3 # 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 joko 1.1 return;
368     }
369 joko 1.3 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 joko 1.1 }
373    
374 joko 1.3 return 1;
375 joko 1.2
376     }
377 joko 1.1
378    
379 joko 1.2
380 joko 1.3 sub _prepare_sync {
381 joko 1.2
382     my $self = shift;
383 joko 1.1
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 joko 1.3 #print "dir: ", $self->{args}->{direction}, "\n";
390    
391 joko 1.1 # manipulate metainfo according to direction of synchronization
392 joko 1.3 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 joko 1.1 ($self->{meta}->{source}, $self->{meta}->{target}) =
399     ($self->{meta}->{target}, $self->{meta}->{source});
400 joko 1.3
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 joko 1.1 } else {
407 joko 1.3 # TODO: are there any other synchronization modes besides PULL, PUSH, FULL?
408    
409 joko 1.1 }
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 joko 1.2
428     return 1;
429 joko 1.1
430 joko 1.3 }
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 joko 1.1 }
447    
448     1;

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