/[cvs]/nfo/perl/libs/Data/Transfer/Sync/StorageInterface.pm
ViewVC logotype

Annotation of /nfo/perl/libs/Data/Transfer/Sync/StorageInterface.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations)
Fri Feb 21 01:47:53 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.5: +7 -4 lines
renamed core function

1 joko 1.6 ## $Id: StorageInterface.pm,v 1.5 2003/02/20 20:24:33 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: StorageInterface.pm,v $
9 joko 1.6 ## Revision 1.5 2003/02/20 20:24:33 joko
10     ## + additional pre-flight checks
11     ##
12 joko 1.5 ## Revision 1.4 2003/02/14 14:14:38 joko
13     ## + new code refactored here
14     ##
15 joko 1.4 ## Revision 1.3 2003/02/11 07:54:55 joko
16     ## + modified module usage
17     ## + debugging trials
18     ##
19 joko 1.3 ## Revision 1.2 2003/02/09 05:05:58 joko
20     ## + major structure changes
21     ## - refactored code to sister modules
22     ## + refactored code to this place
23     ##
24 joko 1.2 ## Revision 1.1 2003/01/20 16:58:46 joko
25     ## + initial check-in: here they are....
26     ##
27 joko 1.1 ## Revision 1.1 2003/01/19 01:23:04 joko
28     ## + new from Data/Transfer/Sync.pm
29     ##
30     ## ----------------------------------------------------------------------------------------
31    
32    
33     package Data::Transfer::Sync::StorageInterface;
34    
35     use strict;
36     use warnings;
37    
38     use mixin::with qw( Data::Transfer::Sync );
39    
40    
41     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
42    
43 joko 1.2 use Data::Dumper;
44     use Hash::Merge qw( merge );
45 joko 1.3 use libdb qw( hash2Sql );
46 joko 1.6 use Data::Transform::Deep qw( merge_to );
47 joko 1.2
48    
49 joko 1.1 # get logger instance
50     my $logger = Log::Dispatch::Config->instance;
51    
52     # this is a shortcut method
53     # ... let's try to avoid _any_ redundant code in here (ok... - at the cost of method lookups...)
54     sub _getNodeList {
55     my $self = shift;
56     my $descent = shift;
57     my $filter = shift;
58 joko 1.2 $logger->debug( __PACKAGE__ . "->_getNodeList( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
59 joko 1.1 #$results ||= $self->{source}->getListUnfiltered($self->{meta}->{source}->{node});
60     #$results ||= $self->{meta}->{source}->{storage}->getListUnfiltered($self->{meta}->{source}->{node});
61 joko 1.2 my $list = $self->{meta}->{$descent}->{storage}->getListFiltered($self->{meta}->{$descent}->{accessorName}, $filter);
62 joko 1.1 #print Dumper($list);
63     return $list;
64     }
65    
66     sub _resolveNodeIdent {
67     my $self = shift;
68     my $descent = shift;
69    
70 joko 1.3 # trace
71     #print Dumper($self->{node}->{$descent});
72     #print Dumper($self);
73     #exit;
74    
75 joko 1.2 $logger->debug( __PACKAGE__ . "->_resolveNodeIdent( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
76 joko 1.1
77     # get to the payload
78     #my $item = $specifier->{item};
79     my $payload = $self->{node}->{$descent}->{payload};
80    
81     # resolve method to get to the id of the given item
82     # we use global metadata and the given descent for this task
83     #my $ident = $self->{$descent}->id($item);
84     #my $ident = $self->{meta}->{$descent}->{storage}->id($item);
85    
86 joko 1.3 # trace
87     #print Dumper($self->{meta}->{$descent});
88     #exit;
89    
90 joko 1.1 my $ident;
91     my $provider_method = $self->{meta}->{$descent}->{IdentProvider}->{method};
92     my $provider_arg = $self->{meta}->{$descent}->{IdentProvider}->{arg};
93    
94 joko 1.2 # trace
95     #print "provider_method: $provider_method", "\n";
96     #print "provider_arg: $provider_arg", "\n";
97     #print Dumper($payload);
98 joko 1.1
99     # resolve to ident
100     if (lc $provider_method eq 'property') {
101     $ident = $payload->{$provider_arg};
102    
103     } elsif (lc $provider_method eq 'storage_method') {
104     #$ident = $self->{meta}->{$descent}->{storage}->id($item);
105     $ident = $self->{meta}->{$descent}->{storage}->$provider_arg($payload);
106    
107     } elsif (lc $provider_method eq 'code') {
108     $ident = 'ac';
109    
110     }
111    
112     $self->{node}->{$descent}->{ident} = $ident;
113    
114     return 1 if $ident;
115    
116     }
117    
118     # TODO:
119     # this should be split up into...
120     # - a "_statNode" (should just touch the node to check for existance)
121     # - a "_loadNode" (should load node completely)
122     # - maybe additionally a "loadNodeProperty" (may specify properties to load)
123     # - introduce $self->{nodecache} for this purpose
124     # TODO:
125     # should we:
126     # - not pass ident in here but resolve it via "$descent"?
127     # - refactor this and stuff it with additional debug/error message
128     # - this = the way the implicit load mechanism works
129     sub _statloadNode {
130    
131     my $self = shift;
132     my $descent = shift;
133     my $ident = shift;
134     my $force = shift;
135    
136 joko 1.5 =pod
137     #print "isa: ", UNIVERSAL::isa($self->{meta}->{$descent}->{storage}), "\n";
138    
139     # this seems to be the first time we access this side,
140     # so just check (again) for a valid storage handle
141     if (! ref $self->{meta}->{$descent}->{storage}) {
142     $logger->critical( __PACKAGE__ . "->_statloadNode( descent=$descent ident=$ident ): Storage handle undefined!" );
143     return;
144     }
145     =cut
146    
147 joko 1.3 #$logger->debug( __PACKAGE__ . "->_statloadNode( descent=$descent ident=$ident )" );
148 joko 1.2
149 joko 1.1 # fetch entry to retrieve checksum from
150     # was:
151     if (!$self->{node}->{$descent} || $force) {
152     # is:
153     #if (!$self->{node}->{$descent}->{item} || $force) {
154    
155     if (!$ident) {
156     #print "\n", "Attempt to fetch entry implicitely by ident failed: no ident given! This may result in an insert if no write-protection is in the way.", "\n";
157     return;
158     }
159    
160     # patch for DBD::CSV
161     if ($ident && $ident eq 'Null') {
162     return;
163     }
164    
165 joko 1.2 #print Dumper($self->{meta});
166 joko 1.1
167     my $query = {
168 joko 1.2 node => $self->{meta}->{$descent}->{accessorName},
169 joko 1.1 subnodes => [qw( cs )],
170     criterias => [
171     { key => $self->{meta}->{$descent}->{IdentProvider}->{arg},
172     op => 'eq',
173     val => $ident },
174     ]
175     };
176    
177 joko 1.2 # trace
178     #print "query:", "\n";
179     #print Dumper($query);
180    
181     # send query and fetch first entry from result
182     my $result = $self->{meta}->{$descent}->{storage}->sendQuery($query);
183     my $entry = $result->getNextEntry();
184    
185     # trace
186     #print Dumper($entry);
187     #print "pers: " . $self->{meta}->{$descent}->{storage}->is_persistent($entry), "\n";
188     #my $state = $self->{meta}->{$descent}->{storage}->_fetch_object_state($entry, { name => 'TransactionHop' } );
189     #print Dumper($state);
190 joko 1.1
191 joko 1.2 # be informed about the status of the query
192     my $status = $result->getStatus();
193 joko 1.1
194     #print Dumper($status);
195    
196     # TODO: enhance error handling (store inside tc)
197     #if (!$row) {
198     # print "\n", "row error", "\n";
199     # next;
200     #}
201    
202     # these checks run before actually loading payload- and meta-data to node-container
203    
204     # 1st level - hard error
205     if ($status && $status->{err}) {
206     $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - hard error (that's ok): $status->{err}" );
207     return;
208     }
209    
210     # 2nd level - logical (empty/notfound) error
211     if (($status && $status->{empty}) || !$entry) {
212     $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - logical error (that's ok)" );
213     #print "no entry (logical)", "\n";
214     return;
215     }
216    
217     #print Dumper($entry);
218    
219     # was:
220     # $self->{node}->{$descent}->{ident} = $ident;
221     # is:
222     # TODO: re-resolve ident from entry via metadata "IdentProvider" here - like elsewhere
223     $self->{node}->{$descent}->{ident} = $ident;
224     $self->{node}->{$descent}->{payload} = $entry;
225    
226     }
227    
228     return 1;
229    
230 joko 1.2 }
231    
232    
233     sub _touchNodeSet {
234     my $self = shift;
235    
236     $logger->debug( __PACKAGE__ . "->touchNodeSet" );
237    
238     # check descents/nodes: does descent exist / is node available?
239     foreach my $descent (keys %{$self->{meta}}) {
240    
241     # 1. check metadata of descent(s)
242     if (!$self->{meta}->{$descent}) {
243     $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not find descent '$descent' in configuration metadata." );
244     next;
245     }
246    
247     # 2. check storage handle(s)
248     my $dbkey = $self->{meta}->{$descent}->{dbKey};
249     if (!$self->{meta}->{$descent}->{storage}) {
250     $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not access storage ( descent='$descent', dbKey='$dbkey' ) - configuration-error?" );
251     next;
252     }
253    
254 joko 1.5 # 2.b check storage handle type
255     my $dbType = $self->{meta}->{$descent}->{storage}->{locator}->{type};
256     if (!$dbType) {
257     $logger->critical( __PACKAGE__ . "->touchNodeSet: Storage ( descent='$descent', dbKey='$dbkey' ) has no 'dbType' - configuration-error?" );
258     next;
259     }
260    
261 joko 1.2 # 3. check if descents (and nodes?) are actually available....
262     # TODO:
263     # eventually pre-check mode of access-attempt (read/write) here to provide an "early-croak" if possible
264    
265     # trace
266     # print Dumper($self->{meta}->{$descent}->{storage}->{locator});
267    
268    
269     my $nodeName = $self->{meta}->{$descent}->{nodeName};
270     my $accessorType = $self->{meta}->{$descent}->{accessorType};
271     my $accessorName = $self->{meta}->{$descent}->{accessorName};
272    
273    
274     # 4. check nodeset
275    
276     # debug message containing database type and used/determined accessor name
277     $logger->debug( __PACKAGE__ . "->touchNodeSet: Accessing dbType=$dbType, accessorName=$accessorName" );
278    
279     # if target node(s) do(es) not exist, check if we should create it automagically
280     if ($dbType ne 'DBI' && !$self->{meta}->{$descent}->{storage}->existsChildNode($accessorName)) {
281    
282     if ($descent eq 'target' && $self->{options}->{target}->{autocreateFolders}) {
283     if (!$self->{meta}->{$descent}->{storage}->createChildNode($accessorName)) {
284     $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not create node '$self->{meta}->{$descent}->{nodeName}\@$self->{meta}->{$descent}->{dbKey}' [$self->{meta}->{$descent}->{nodeType}]." );
285     next;
286     }
287     } else {
288     $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not reach node \"$nodeName\" (accessorName=$accessorName, accessorType=$accessorType) at descent \"$descent\"" );
289     next;
290     }
291     }
292    
293     }
294    
295     # trace
296     #print Dumper($self->{meta});
297     #exit;
298    
299    
300     return 1;
301    
302     }
303    
304    
305    
306     sub _modifyNode {
307     my $self = shift;
308     my $descent = shift;
309     my $action = shift;
310     my $map = shift;
311     my $crit = shift;
312    
313     # map for new style callbacks
314     my $map_callbacks = {};
315    
316     # checks go first!
317    
318     # TODO: this should be reviewed first - before extending ;-)
319     # TODO: this should be extended:
320     # count this cases inside the caller to this sub and provide a better overall message
321     # if this counts still zero in the end:
322     # "No nodes have been touched for modify: Do you have column-headers in your csv file?"
323     if (not defined $self->{node}) {
324     #$logger->critical( __PACKAGE__ . "->_modifyNode failed: \"$descent\" node is empty." );
325     #return;
326     }
327    
328     # transfer callback nodes from value map to callback map - handle them afterwards! - (new style callbacks)
329     if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {
330     foreach my $callback (keys %{$callbacks->{write}}) {
331     $map_callbacks->{write}->{$callback} = $map->{$callback};
332     delete $map->{$callback};
333     }
334     }
335    
336    
337     # trace
338     #print Dumper($self->{meta});
339    
340    
341     # --------------------------------------------------------------------------
342     # DBI speaks SQL
343     if ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'DBI') {
344    
345     # trace
346     #print Dumper($map);
347     #delete $map->{cs};
348    
349     # transfer data
350     # TODO: wrap this around '$storageHandle->sendQuery(...)'!?
351     my $sql_main;
352     if (lc($action) eq 'insert') {
353     $sql_main = hash2Sql($self->{meta}->{$descent}->{accessorName}, $map, 'SQL_INSERT');
354     } elsif (lc $action eq 'update') {
355     $crit ||= "$self->{meta}->{$descent}->{IdentProvider}->{arg}='$self->{node}->{$descent}->{ident}'";
356     $sql_main = hash2Sql($self->{meta}->{$descent}->{accessorName}, $map, 'SQL_UPDATE', $crit);
357     }
358     my $sqlHandle = $self->{meta}->{$descent}->{storage}->sendCommand($sql_main);
359    
360     # handle errors
361     if ($sqlHandle->err) {
362     #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
363     $self->{node}->{status}->{error} = {
364     statement => $sql_main,
365     state => $sqlHandle->state,
366     err => $sqlHandle->err,
367     errstr => $sqlHandle->errstr,
368     };
369     } else {
370     $self->{node}->{status}->{ok} = 1;
371     }
372    
373    
374     # --------------------------------------------------------------------------
375     # Tangram does it the oo-way (naturally)
376     } elsif ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'Tangram') {
377     my $sql_main;
378     my $object;
379    
380     # determine classname
381     my $classname = $self->{meta}->{$descent}->{nodeType};
382    
383     # attributes/properties to exclude
384     # push declared ones from metadata
385     my @exclude = @{$self->{meta}->{$descent}->{subnodes_exclude}};
386     # push the attributes associated with the identifier
387     if (my $identProvider = $self->{meta}->{$descent}->{IdentProvider}) {
388     push @exclude, $identProvider->{arg};
389     }
390    
391     # trace
392     #print Dumper($self->{meta});
393     #exit;
394    
395     # new feature:
396     # - check TypeProvider metadata property from other side
397     # - use argument (arg) inside as a classname for object creation on this side
398     #my $otherSide = $self->_otherSide($descent);
399     if (my $typeProvider = $self->{meta}->{$descent}->{TypeProvider}) {
400     #print Dumper($map);
401     $classname = $map->{$typeProvider->{arg}};
402     # remove nodes from map also (push nodes to "subnodes_exclude" list)
403     push @exclude, $typeProvider->{arg};
404     }
405    
406     # exclude banned properties (remove from map)
407     #map { delete $self->{node}->{map}->{$_} } @{$self->{args}->{exclude}};
408     map { delete $map->{$_} } @exclude;
409    
410     # list of properties
411     my @props = keys %{$map};
412    
413     # transfer data
414     if (lc $action eq 'insert') {
415    
416     # make the object persistent in four steps:
417     # - raw create (perl / class tangram scope)
418     # - orm insert (tangram scope) ... this establishes inheritance - don't try to fill in inherited properties before!
419     # is this a Tangram bug?
420     # - raw fill-in from hash (perl scope)
421     # - orm update (tangram scope) ... this updates all properties just filled in
422    
423    
424     # ==========================
425     # TODO: REVIEW HERE!!!
426     # can't we achieve this more elegant?
427     # o use DesignPattern::Object???
428     # o use Hash::Merge!!! (take care about the cloning behaviour)
429    
430     # check if object exists (is classname a valid perl package/module?)
431    
432     # we can just check if the classname is valid here
433     if (!$classname) {
434     $logger->critical( __PACKAGE__ . "->_modifyNode: classname is undefined" );
435     # FIXME: stop syncing here?
436     return;
437     }
438    
439     # try to match against the classes known by Class::Tangram
440     # FIXME: do "/i" on win32 only!
441     my $classname_find = quotemeta($classname);
442     if (!grep(m/$classname_find/i, Class::Tangram::known_classes())) {
443     $logger->critical( __PACKAGE__ . "->_modifyNode: Classname '$classname' is not known by Class::Tangram" );
444     # FIXME: stop syncing here?
445     return;
446     }
447    
448     # create new object ...
449     # V1
450     # build array to initialize object
451     #my @initarray = ();
452     #map { push @initarray, $_, undef; } @props;
453     #my $object = $classname->new( @initarray );
454     # V2
455     $object = $classname->new();
456     # V3
457     # $object = DesignPattern::Object->new($data);
458    
459     # ... pass to orm first ...
460     $self->{meta}->{$descent}->{storage}->insert($object);
461    
462     # ... and initialize with empty (undef'd) properties afterwards.
463     map { $object->{$_} = undef; } @props;
464    
465     # trace
466     #print "\n";
467     #print Dumper($map);
468     #print Dumper($object);
469     #exit;
470    
471     # mix in (merge) values ...
472     # TODO: use Hash::Merge here? benchmark!
473     # no! we'd need a Object::Merge here! it's *...2object*
474 joko 1.6 merge_to($object, $map);
475 joko 1.2
476     # trace
477     #print Dumper($object);
478     #exit;
479    
480     # TODO: REVIEW HERE!!!
481     # ==========================
482    
483     # ... and re-update@orm.
484     $self->{meta}->{$descent}->{storage}->update($object);
485    
486     # asymmetry: get ident after insert
487     # TODO:
488     # - just do this if it is an IdentAuthority
489     # - use IdentProvider metadata here
490     #print Dumper($self->{meta}->{$descent});
491     my $oid = $self->{meta}->{$descent}->{storage}->id($object);
492     #print "oid: $oid", "\n";
493     $self->{node}->{$descent}->{ident} = $oid;
494    
495    
496     } elsif (lc $action eq 'update') {
497    
498     # get fresh object from orm first
499     $object = $self->{meta}->{$descent}->{storage}->load($self->{node}->{$descent}->{ident});
500    
501     #print Dumper($self->{node});
502    
503     # mix in values
504     #print Dumper($object);
505     # TODO: use Hash::Merge here???
506 joko 1.6 merge_to($object, $map);
507 joko 1.2 #print Dumper($object);
508     #exit;
509    
510     # update orm
511     $self->{meta}->{$descent}->{storage}->update($object);
512    
513     }
514    
515     #exit;
516    
517     my $error = 0;
518    
519     # handle new style callbacks - this is a HACK - do this without an eval!
520     #print Dumper($map);
521     #print "cb: ", Dumper($self->{meta}->{$descent}->{Callback});
522     #print Dumper($map_callbacks);
523     foreach my $node (keys %{$map_callbacks->{write}}) {
524     #print Dumper($node);
525 joko 1.4
526     # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
527     my $perl_callback = $self->{meta}->{$descent}->{nodeType} . '::' . $node . '_write';
528 joko 1.2 my $evalstring = $perl_callback . '( { object => $object, value => $map_callbacks->{write}->{$node}, storage => $self->{meta}->{$descent}->{storage} } );';
529     #print $evalstring, "\n"; exit;
530     eval($evalstring);
531     if ($@) {
532     $error = 1;
533 joko 1.4 $logger->error( __PACKAGE__ . "->_modifyNode: $@" );
534     next;
535 joko 1.2 }
536 joko 1.4 # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
537 joko 1.2
538     #print "after eval", "\n";
539    
540     if (!$error) {
541     # re-update@orm
542     $self->{meta}->{$descent}->{storage}->update($object);
543     }
544     }
545    
546     # handle errors
547     if ($error) {
548     #print "error", "\n";
549     =pod
550     my $sqlHandle;
551     #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
552     $self->{node}->{status}->{error} = {
553     statement => $sql_main,
554     state => $sqlHandle->state,
555     err => $sqlHandle->err,
556     errstr => $sqlHandle->errstr,
557     };
558     =cut
559     # rollback....
560     #print "rollback", "\n";
561     $self->{meta}->{$descent}->{storage}->erase($object);
562     #print "after rollback", "\n";
563     } else {
564     $self->{node}->{status}->{ok} = 1;
565     }
566    
567     }
568    
569 joko 1.1 }
570    
571 joko 1.4 sub _erase_all {
572     my $self = shift;
573     my $descent = shift;
574     #my $node = shift;
575     #print Dumper($self->{meta}->{$descent});
576     #my $node = $self->{meta}->{$descent}->{nodeName};
577     my $node = $self->{meta}->{$descent}->{accessorName};
578     $logger->debug( __PACKAGE__ . "->_erase_all( node $node )" );
579     $self->{meta}->{$descent}->{storage}->eraseAll($node);
580     }
581 joko 1.1
582     1;
583 joko 1.4 __END__

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