/[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.3 - (hide annotations)
Tue Feb 11 07:54:55 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.2: +18 -6 lines
+ modified module usage
+ debugging trials

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

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