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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show 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 ## $Id: StorageInterface.pm,v 1.5 2003/02/20 20:24:33 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: StorageInterface.pm,v $
9 ## Revision 1.5 2003/02/20 20:24:33 joko
10 ## + additional pre-flight checks
11 ##
12 ## Revision 1.4 2003/02/14 14:14:38 joko
13 ## + new code refactored here
14 ##
15 ## Revision 1.3 2003/02/11 07:54:55 joko
16 ## + modified module usage
17 ## + debugging trials
18 ##
19 ## 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 ## Revision 1.1 2003/01/20 16:58:46 joko
25 ## + initial check-in: here they are....
26 ##
27 ## 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 use Data::Dumper;
44 use Hash::Merge qw( merge );
45 use libdb qw( hash2Sql );
46 use Data::Transform::Deep qw( merge_to );
47
48
49 # 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 $logger->debug( __PACKAGE__ . "->_getNodeList( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
59 #$results ||= $self->{source}->getListUnfiltered($self->{meta}->{source}->{node});
60 #$results ||= $self->{meta}->{source}->{storage}->getListUnfiltered($self->{meta}->{source}->{node});
61 my $list = $self->{meta}->{$descent}->{storage}->getListFiltered($self->{meta}->{$descent}->{accessorName}, $filter);
62 #print Dumper($list);
63 return $list;
64 }
65
66 sub _resolveNodeIdent {
67 my $self = shift;
68 my $descent = shift;
69
70 # trace
71 #print Dumper($self->{node}->{$descent});
72 #print Dumper($self);
73 #exit;
74
75 $logger->debug( __PACKAGE__ . "->_resolveNodeIdent( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
76
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 # trace
87 #print Dumper($self->{meta}->{$descent});
88 #exit;
89
90 my $ident;
91 my $provider_method = $self->{meta}->{$descent}->{IdentProvider}->{method};
92 my $provider_arg = $self->{meta}->{$descent}->{IdentProvider}->{arg};
93
94 # trace
95 #print "provider_method: $provider_method", "\n";
96 #print "provider_arg: $provider_arg", "\n";
97 #print Dumper($payload);
98
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 =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 #$logger->debug( __PACKAGE__ . "->_statloadNode( descent=$descent ident=$ident )" );
148
149 # 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 #print Dumper($self->{meta});
166
167 my $query = {
168 node => $self->{meta}->{$descent}->{accessorName},
169 subnodes => [qw( cs )],
170 criterias => [
171 { key => $self->{meta}->{$descent}->{IdentProvider}->{arg},
172 op => 'eq',
173 val => $ident },
174 ]
175 };
176
177 # 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
191 # be informed about the status of the query
192 my $status = $result->getStatus();
193
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 }
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 # 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 # 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 merge_to($object, $map);
475
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 merge_to($object, $map);
507 #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
526 # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
527 my $perl_callback = $self->{meta}->{$descent}->{nodeType} . '::' . $node . '_write';
528 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 $logger->error( __PACKAGE__ . "->_modifyNode: $@" );
534 next;
535 }
536 # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
537
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 }
570
571 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
582 1;
583 __END__

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